125 lines
3.8 KiB
Python
125 lines
3.8 KiB
Python
"""
|
||
文件上传路由
|
||
"""
|
||
|
||
import logging
|
||
import os
|
||
import shutil
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
|
||
from fastapi import APIRouter, UploadFile, File, Form, HTTPException, status
|
||
from pydantic import BaseModel
|
||
|
||
from app.core.config import settings
|
||
|
||
logger = logging.getLogger(__name__)
|
||
router = APIRouter()
|
||
|
||
|
||
class UploadResponse(BaseModel):
|
||
"""上传响应模型"""
|
||
success: bool
|
||
filename: str
|
||
file_type: str
|
||
original_filename: str
|
||
task_description: str
|
||
message: Optional[str] = None
|
||
|
||
|
||
class UploadImageResponse(BaseModel):
|
||
"""上传图片响应模型"""
|
||
success: bool
|
||
filename: str
|
||
file_type: str
|
||
original_filename: str
|
||
original_image: str
|
||
task_description: str
|
||
message: str
|
||
|
||
|
||
def allowed_file(filename: str) -> bool:
|
||
"""检查文件是否被允许"""
|
||
if '.' not in filename:
|
||
return False
|
||
ext = filename.rsplit('.', 1)[1].lower()
|
||
return ext in settings.ALLOWED_EXTENSIONS
|
||
|
||
|
||
@router.post("/upload", response_model=UploadResponse, summary="上传CSV或图片文件")
|
||
async def upload_file(
|
||
file: UploadFile = File(...),
|
||
task_description: str = Form(default="时间序列数据分析")
|
||
) -> dict:
|
||
"""
|
||
上传数据文件(CSV 或图片)
|
||
|
||
- **file**: CSV 或图片文件 (PNG, JPG, BMP, TIFF)
|
||
- **task_description**: 分析任务描述
|
||
"""
|
||
try:
|
||
logger.info(f"=== 上传请求开始 ===")
|
||
logger.info(f"文件名: {file.filename}")
|
||
logger.info(f"任务描述: {task_description}")
|
||
|
||
# 检查文件名
|
||
if not file.filename:
|
||
logger.error("文件名为空")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
detail="没有选择文件"
|
||
)
|
||
|
||
# 检查文件类型
|
||
if not allowed_file(file.filename):
|
||
logger.error(f"不支持的文件类型: {file.filename}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
detail=f"不支持的文件类型。允许的类型: {', '.join(settings.ALLOWED_EXTENSIONS)}"
|
||
)
|
||
|
||
# 获取文件扩展名
|
||
file_ext = file.filename.rsplit('.', 1)[1].lower()
|
||
|
||
# 生成文件名
|
||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||
new_filename = f"upload_{timestamp}_{file.filename}"
|
||
|
||
# 保存文件
|
||
file_path = settings.get_upload_path(new_filename)
|
||
logger.info(f"保存文件到: {file_path}")
|
||
|
||
content = await file.read()
|
||
with open(file_path, 'wb') as f:
|
||
f.write(content)
|
||
|
||
logger.info(f"文件保存成功,大小: {len(content)} bytes")
|
||
|
||
# 处理不同的文件类型
|
||
if file_ext == 'csv':
|
||
logger.info("处理 CSV 文件")
|
||
return {
|
||
"success": True,
|
||
"filename": new_filename,
|
||
"file_type": "csv",
|
||
"original_filename": file.filename,
|
||
"task_description": task_description
|
||
}
|
||
|
||
else:
|
||
logger.warning(f"不支持的文件类型: {file_ext}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
detail=f"目前只支持 CSV 文件。您上传的是: {file_ext}"
|
||
)
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"上传处理异常: {str(e)}", exc_info=True)
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail=str(e)
|
||
)
|