""" 文件上传路由 """ 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) )