guzhi/app/controllers/upload.py
邹方成 f536178428 feat: 新增交易记录管理功能与统一上传接口
feat(交易记录): 新增交易记录管理页面与API接口
feat(上传): 添加统一上传接口支持自动识别文件类型
feat(用户管理): 为用户模型添加备注字段并更新相关接口
feat(邮件): 实现SMTP邮件发送功能并添加测试脚本
feat(短信): 增强短信服务配置灵活性与日志记录

fix(发票): 修复发票列表时间筛选功能
fix(nginx): 调整上传大小限制与超时配置

docs: 添加多个功能模块的说明文档
docs(估值): 补充估值计算流程与API提交数据说明

chore: 更新依赖与Docker镜像版本
2025-11-20 20:53:09 +08:00

102 lines
3.5 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和文件名
"""
# 检查文件类型
if not file.content_type.startswith('image/'):
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",
}
if file.content_type not in allowed:
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
"""
if file.content_type and file.content_type.startswith("image/"):
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)