guzhi/app/controllers/upload.py
邹方成 c690a95cab feat: 新增发票管理模块和用户端接口
refactor: 优化响应格式和错误处理

fix: 修复文件上传类型校验和删除无用PDF文件

perf: 添加估值评估审核时间字段和查询条件

docs: 更新Docker镜像版本至v1.8

test: 添加响应格式检查脚本

style: 统一API响应数据结构

chore: 清理无用静态文件和更新构建脚本
2025-11-24 16:39:53 +08:00

130 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
from pathlib import Path
from typing import List
from fastapi import UploadFile
from app.schemas.upload import ImageUploadResponse, FileUploadResponse
from app.settings.config import settings
class UploadController:
"""文件上传控制器"""
@staticmethod
async def upload_image(file: UploadFile) -> ImageUploadResponse:
"""
上传图片
:param file: 上传的图片文件
:return: 图片URL和文件名
"""
ext = os.path.splitext(file.filename or "")[1].lower()
image_exts = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".svg"}
if not (file.content_type.startswith('image/') or ext in image_exts):
raise ValueError("只支持上传图片文件")
# 获取项目根目录
base_dir = Path(__file__).resolve().parent.parent
# 图片保存目录
upload_dir = base_dir / "static" / "images"
# 确保目录存在
if not upload_dir.exists():
upload_dir.mkdir(parents=True, exist_ok=True)
# 生成文件名
filename = file.filename
file_path = upload_dir / filename
# 如果文件已存在,重命名
counter = 1
while file_path.exists():
name, ext = os.path.splitext(filename)
filename = f"{name}_{counter}{ext}"
file_path = upload_dir / filename
counter += 1
# 保存文件
content = await file.read()
with open(file_path, "wb") as f:
f.write(content)
# 返回完整的可访问URL
return ImageUploadResponse(
url=f"{settings.BASE_URL}/static/images/{filename}",
filename=filename
)
@staticmethod
async def upload_file(file: UploadFile) -> FileUploadResponse:
allowed = {
"application/pdf",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/msword",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-excel",
"application/zip",
"application/x-zip-compressed",
"application/octet-stream",
"text/plain",
"text/csv",
"application/json",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/x-rar-compressed",
"application/x-7z-compressed",
}
allowed_exts = {
".pdf",
".doc",
".docx",
".xls",
".xlsx",
".zip",
".rar",
".7z",
".txt",
".csv",
".ppt",
".pptx",
".json",
}
ext = os.path.splitext(file.filename or "")[1].lower()
if (file.content_type not in allowed) and (ext not in allowed_exts):
raise ValueError("不支持的文件类型")
base_dir = Path(__file__).resolve().parent.parent
upload_dir = base_dir / "static" / "files"
if not upload_dir.exists():
upload_dir.mkdir(parents=True, exist_ok=True)
filename = file.filename
file_path = upload_dir / filename
counter = 1
while file_path.exists():
name, ext = os.path.splitext(filename)
filename = f"{name}_{counter}{ext}"
file_path = upload_dir / filename
counter += 1
content = await file.read()
with open(file_path, "wb") as f:
f.write(content)
return FileUploadResponse(
url=f"{settings.BASE_URL}/static/files/{filename}",
filename=filename,
content_type=file.content_type,
)
@staticmethod
async def upload_any(file: UploadFile) -> FileUploadResponse:
"""
统一上传入口,自动识别图片与非图片类型。
返回统一结构url, filename, content_type
"""
image_exts = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".svg"}
ext = os.path.splitext(file.filename or "")[1].lower()
if (file.content_type and file.content_type.startswith("image/")) or (ext in image_exts):
img = await UploadController.upload_image(file)
return FileUploadResponse(url=img.url, filename=img.filename, content_type=file.content_type or "image")
# 非图片类型复用原文件上传校验
return await UploadController.upload_file(file)