Files
2026-07-27 09:57:35 +08:00

73 lines
2.7 KiB
Python

import mimetypes
from pathlib import Path
from uuid import uuid4
from fastapi import APIRouter, HTTPException, Request, status
from starlette.datastructures import UploadFile
from app.config import oss_settings
from app.schemas import UploadResponse
router = APIRouter(prefix="/uploads", tags=["uploads"])
MAX_IMAGE_SIZE = 5 * 1024 * 1024
ALLOWED_IMAGE_TYPES = {"image/jpeg", "image/png", "image/webp", "image/gif"}
def get_public_url(object_key: str) -> str:
if oss_settings.public_base_url:
return f"{oss_settings.public_base_url.rstrip('/')}/{object_key}"
endpoint = oss_settings.endpoint.replace("https://", "").replace("http://", "").rstrip("/")
return f"https://{oss_settings.bucket_name}.{endpoint}/{object_key}"
def build_object_key(filename: str | None, folder: str) -> str:
suffix = Path(filename or "").suffix.lower()
if not suffix:
suffix = mimetypes.guess_extension("image/jpeg") or ".jpg"
safe_folder = folder.strip("/ ") or oss_settings.upload_prefix
return f"{oss_settings.upload_prefix.strip('/')}/{safe_folder}/{uuid4().hex}{suffix}"
@router.post("/images", response_model=UploadResponse)
async def upload_image(request: Request) -> UploadResponse:
if not oss_settings.is_configured:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="OSS 配置未完成,请先填写 app/config.py 中的 OSS 配置。",
)
form = await request.form()
file = form.get("file")
folder_value = form.get("folder")
folder = str(folder_value) if folder_value else "products"
if not isinstance(file, UploadFile):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="请选择要上传的图片")
content_type = file.content_type or "application/octet-stream"
if content_type not in ALLOWED_IMAGE_TYPES:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="只支持上传图片文件")
data = await file.read()
if len(data) > MAX_IMAGE_SIZE:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="图片大小不能超过 5MB")
try:
import oss2
except ImportError as exc:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="缺少 oss2 依赖,请先安装 requirements.txt 中的依赖。",
) from exc
object_key = build_object_key(file.filename, folder)
auth = oss2.Auth(oss_settings.access_key_id, oss_settings.access_key_secret)
bucket = oss2.Bucket(auth, oss_settings.upload_endpoint, oss_settings.bucket_name)
bucket.put_object(object_key, data, headers={"Content-Type": content_type})
return UploadResponse(url=get_public_url(object_key), object_key=object_key)