91 lines
3.1 KiB
Python
91 lines
3.1 KiB
Python
"""文件路由:上传 / 列表 / 下载 / 删除。"""
|
|
import hashlib
|
|
import uuid
|
|
from pathlib import Path
|
|
|
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
|
|
from fastapi.responses import FileResponse
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ..config import settings
|
|
from ..core.deps import get_current_user
|
|
from ..core.response import ok
|
|
from ..database import get_db
|
|
from ..models import FileEntry, User
|
|
|
|
router = APIRouter(prefix="/files", tags=["文件"])
|
|
|
|
|
|
def _file_out(f: FileEntry) -> dict:
|
|
return {"id": f.id, "project_id": f.project_id, "filename": f.filename, "size": f.size,
|
|
"mime": f.mime, "created_at": f.created_at.isoformat()}
|
|
|
|
|
|
@router.post("/upload")
|
|
async def upload_file(
|
|
file: UploadFile = File(...),
|
|
project_id: int | None = Form(None),
|
|
user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
data = await file.read()
|
|
if len(data) > settings.MAX_UPLOAD_MB * 1024 * 1024:
|
|
raise HTTPException(status_code=413, detail=f"文件超过 {settings.MAX_UPLOAD_MB}MB 限制")
|
|
|
|
stored_name = f"{uuid.uuid4().hex}_{file.filename}"
|
|
target = settings.UPLOAD_DIR / stored_name
|
|
target.write_bytes(data)
|
|
|
|
entry = FileEntry(
|
|
user_id=user.id, project_id=project_id, filename=file.filename,
|
|
stored_path=str(target), size=len(data), mime=file.content_type or "",
|
|
sha256=hashlib.sha256(data).hexdigest(),
|
|
)
|
|
db.add(entry)
|
|
db.commit()
|
|
db.refresh(entry)
|
|
return ok(_file_out(entry))
|
|
|
|
|
|
@router.get("")
|
|
def list_files(user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
|
files = db.query(FileEntry).filter(FileEntry.user_id == user.id).order_by(FileEntry.id.desc()).all()
|
|
return ok([_file_out(f) for f in files])
|
|
|
|
|
|
def _get_owned(db: Session, user: User, file_id: int) -> FileEntry:
|
|
f = db.get(FileEntry, file_id)
|
|
if not f or f.user_id != user.id:
|
|
raise HTTPException(status_code=404, detail="文件不存在")
|
|
return f
|
|
|
|
|
|
@router.get("/{file_id}/content")
|
|
def file_content(file_id: int, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
|
"""内联内容(图片预览等,鉴权)。"""
|
|
f = _get_owned(db, user, file_id)
|
|
path = Path(f.stored_path)
|
|
if not path.exists():
|
|
raise HTTPException(status_code=404, detail="文件已丢失")
|
|
return FileResponse(path, media_type=f.mime or "application/octet-stream")
|
|
|
|
|
|
@router.get("/{file_id}/download")
|
|
def download_file(file_id: int, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
|
f = _get_owned(db, user, file_id)
|
|
path = Path(f.stored_path)
|
|
if not path.exists():
|
|
raise HTTPException(status_code=404, detail="文件已丢失")
|
|
return FileResponse(path, filename=f.filename, media_type=f.mime or "application/octet-stream")
|
|
|
|
|
|
@router.delete("/{file_id}")
|
|
def delete_file(file_id: int, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
|
f = _get_owned(db, user, file_id)
|
|
path = Path(f.stored_path)
|
|
if path.exists():
|
|
path.unlink()
|
|
db.delete(f)
|
|
db.commit()
|
|
return ok(message="已删除")
|