102 lines
3.8 KiB
Python
102 lines
3.8 KiB
Python
"""文件上传/读取 — 数据库存储(file_service.py 为可选备用方案,保留不删)"""
|
||
from fastapi import APIRouter, UploadFile, File, Depends, HTTPException, Request
|
||
from fastapi.responses import Response
|
||
from sqlalchemy.orm import Session
|
||
from logger import logger
|
||
from models import get_db, Upload
|
||
from middleware.user_context import UserContext
|
||
# 文件服务模块保留备用(不删除),如需切换取消下面注释即可
|
||
# from file_service import upload_file, download_file, delete_file
|
||
|
||
router = APIRouter(tags=["上传"])
|
||
|
||
ALLOWED = {"image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"}
|
||
MAX_SIZE = 20 * 1024 * 1024 # 20MB
|
||
|
||
|
||
@router.post("/api/upload/image")
|
||
async def upload_image(file: UploadFile = File(...), db: Session = Depends(get_db), request: Request = None):
|
||
"""上传图片 — 存入数据库 LONGBLOB"""
|
||
user_id = UserContext.get_user_id(request)
|
||
try:
|
||
if file.content_type not in ALLOWED:
|
||
raise HTTPException(400, f"不支持的图片格式: {file.content_type}")
|
||
data = await file.read()
|
||
if len(data) > MAX_SIZE:
|
||
raise HTTPException(400, f"图片不能超过 20MB,当前 {len(data)} 字节")
|
||
|
||
filename = file.filename or "img"
|
||
logger.info(f"上传图片: {filename}, {len(data)} bytes, {file.content_type}, user={user_id}")
|
||
|
||
upload = Upload(
|
||
filename=filename,
|
||
content_type=file.content_type,
|
||
data=data,
|
||
size=len(data),
|
||
created_by=user_id,
|
||
)
|
||
db.add(upload)
|
||
db.commit()
|
||
db.refresh(upload)
|
||
|
||
logger.info(f"图片已存入 DB, id={upload.id}")
|
||
return {"url": f"/api/file/{upload.id}"}
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"上传失败: {e}")
|
||
raise HTTPException(500, f"上传失败: {str(e)}")
|
||
|
||
|
||
@router.get("/api/file/{file_id}")
|
||
def get_file(file_id: int, db: Session = Depends(get_db)):
|
||
"""读取图片 — 公开访问(不走网关,供编辑器 <img> 标签和分享页加载)
|
||
|
||
优先从 DB blob 读取;如为文件服务存储则代理下载
|
||
"""
|
||
upload = db.query(Upload).filter_by(id=file_id).first()
|
||
if not upload:
|
||
raise HTTPException(404, "文件不存在")
|
||
|
||
# DB blob(主存储方式)
|
||
if upload.data:
|
||
return Response(content=upload.data, media_type=upload.content_type)
|
||
|
||
# 文件服务存储(兼容之前上传的文件)
|
||
if upload.file_id:
|
||
from file_service import download_file
|
||
try:
|
||
data, content_type, _ = download_file(upload.file_id)
|
||
return Response(content=data, media_type=content_type)
|
||
except FileNotFoundError:
|
||
raise HTTPException(404, "文件在文件服务中不存在")
|
||
except Exception as e:
|
||
logger.error(f"从文件服务下载失败: {e}")
|
||
raise HTTPException(500, f"下载文件失败: {str(e)}")
|
||
|
||
raise HTTPException(404, "文件数据不存在")
|
||
|
||
|
||
@router.delete("/api/file/{file_id}")
|
||
def delete_file_api(file_id: int, db: Session = Depends(get_db), request: Request = None):
|
||
"""删除图片 — 从 DB 和文件服务中删除"""
|
||
user_id = UserContext.get_user_id(request)
|
||
upload = db.query(Upload).filter_by(id=file_id).first()
|
||
if not upload:
|
||
raise HTTPException(404, "文件不存在")
|
||
|
||
# 如果存在文件服务记录,同步删除
|
||
if upload.file_id:
|
||
from file_service import delete_file
|
||
try:
|
||
delete_file(upload.file_id)
|
||
logger.info(f"文件服务删除成功, fileId={upload.file_id}")
|
||
except Exception as e:
|
||
logger.error(f"文件服务删除失败: {e}")
|
||
|
||
db.delete(upload)
|
||
db.commit()
|
||
logger.info(f"图片记录已删除, db_id={file_id}, user={user_id}")
|
||
return {"ok": True}
|