103 lines
3.0 KiB
Python
103 lines
3.0 KiB
Python
"""
|
||
文件处理工具
|
||
"""
|
||
import os
|
||
import shutil
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
from fastapi import UploadFile
|
||
from app.core.config import settings
|
||
from app.core.exceptions import FileUploadException
|
||
from app.utils.logger import logger
|
||
|
||
|
||
def validate_file_extension(filename: str) -> bool:
|
||
"""验证文件扩展名"""
|
||
ext = Path(filename).suffix.lower()
|
||
return ext in settings.allowed_extensions
|
||
|
||
|
||
def validate_file_size(file_size: int) -> bool:
|
||
"""验证文件大小"""
|
||
return file_size <= settings.MAX_UPLOAD_SIZE
|
||
|
||
|
||
async def save_upload_file(file: UploadFile, project_id: str, subdir: Optional[str] = None) -> str:
|
||
"""
|
||
保存上传的文件
|
||
|
||
Args:
|
||
file: 上传的文件
|
||
project_id: 项目ID
|
||
subdir: 子目录(可选)
|
||
|
||
Returns:
|
||
保存的文件路径
|
||
"""
|
||
# 验证文件扩展名
|
||
if not validate_file_extension(file.filename):
|
||
raise FileUploadException(
|
||
f"不支持的文件类型。支持的类型: {', '.join(settings.allowed_extensions)}"
|
||
)
|
||
|
||
# 创建保存目录
|
||
save_dir = Path(settings.UPLOAD_DIR) / project_id
|
||
if subdir:
|
||
save_dir = save_dir / subdir
|
||
save_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
# 保存文件
|
||
file_path = save_dir / file.filename
|
||
try:
|
||
with open(file_path, "wb") as f:
|
||
content = await file.read()
|
||
# 验证文件大小
|
||
if not validate_file_size(len(content)):
|
||
raise FileUploadException(
|
||
f"文件大小超过限制(最大 {settings.MAX_UPLOAD_SIZE / 1024 / 1024:.0f}MB)"
|
||
)
|
||
f.write(content)
|
||
|
||
logger.info(f"文件保存成功: {file_path}")
|
||
return str(file_path)
|
||
|
||
except Exception as e:
|
||
logger.error(f"文件保存失败: {str(e)}")
|
||
raise FileUploadException(f"文件保存失败: {str(e)}")
|
||
|
||
|
||
def cleanup_temp_file(file_path: str) -> None:
|
||
"""清理临时文件"""
|
||
try:
|
||
if os.path.exists(file_path):
|
||
os.remove(file_path)
|
||
logger.info(f"临时文件已删除: {file_path}")
|
||
except Exception as e:
|
||
logger.warning(f"删除临时文件失败: {file_path}, 错误: {str(e)}")
|
||
|
||
|
||
def cleanup_temp_directory(dir_path: str) -> None:
|
||
"""清理临时目录"""
|
||
try:
|
||
if os.path.exists(dir_path):
|
||
shutil.rmtree(dir_path)
|
||
logger.info(f"临时目录已删除: {dir_path}")
|
||
except Exception as e:
|
||
logger.warning(f"删除临时目录失败: {dir_path}, 错误: {str(e)}")
|
||
|
||
|
||
def detect_file_type(filename: str) -> str:
|
||
"""根据文件扩展名检测文件类型"""
|
||
ext = Path(filename).suffix.lower()
|
||
|
||
if ext in [".xlsx", ".xls"]:
|
||
return "excel"
|
||
elif ext in [".docx", ".doc"]:
|
||
return "word"
|
||
elif ext == ".pdf":
|
||
return "pdf"
|
||
elif ext == ".csv":
|
||
return "csv"
|
||
else:
|
||
raise FileUploadException(f"不支持的文件类型: {ext}")
|