2025-08-05 11:57:14 +08:00

209 lines
6.6 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Query
from sqlalchemy.orm import Session
from typing import List, Optional, Dict, Any
import os
import uuid
from datetime import datetime
from core.database import get_db
router = APIRouter()
# 文件上传配置
UPLOAD_DIR = "uploads"
VIDEO_DIR = os.path.join(UPLOAD_DIR, "videos")
IMAGE_DIR = os.path.join(UPLOAD_DIR, "images")
# 确保上传目录存在
os.makedirs(VIDEO_DIR, exist_ok=True)
os.makedirs(IMAGE_DIR, exist_ok=True)
# 允许的文件类型
ALLOWED_VIDEO_TYPES = ["video/mp4", "video/avi", "video/mov", "video/wmv"]
ALLOWED_IMAGE_TYPES = ["image/jpeg", "image/png", "image/gif", "image/bmp"]
# 文件大小限制 (MB)
MAX_VIDEO_SIZE = 100 # 100MB
MAX_IMAGE_SIZE = 10 # 10MB
@router.post("/video", summary="上传视频文件")
async def upload_video(
file: UploadFile = File(...),
device_id: Optional[int] = Form(None),
description: Optional[str] = Form(""),
db: Session = Depends(get_db)
):
"""上传视频文件"""
try:
# 检查文件类型
if file.content_type not in ALLOWED_VIDEO_TYPES:
raise HTTPException(
status_code=400,
detail=f"不支持的文件类型: {file.content_type}"
)
# 检查文件大小
file_size = 0
content = await file.read()
file_size = len(content)
if file_size > MAX_VIDEO_SIZE * 1024 * 1024:
raise HTTPException(
status_code=400,
detail=f"文件大小超过限制: {MAX_VIDEO_SIZE}MB"
)
# 生成唯一文件名
file_id = str(uuid.uuid4())
file_extension = os.path.splitext(file.filename)[1]
filename = f"{file_id}{file_extension}"
file_path = os.path.join(VIDEO_DIR, filename)
# 保存文件
with open(file_path, "wb") as f:
f.write(content)
# TODO: 获取视频时长
duration = 30.5 # 模拟时长
return {
"file_id": file_id,
"file_url": f"/uploads/videos/{filename}",
"file_size": file_size,
"duration": duration,
"device_id": device_id,
"description": description,
"uploaded_at": datetime.now().isoformat()
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"上传视频失败: {str(e)}")
@router.post("/image", summary="上传图片文件")
async def upload_image(
file: UploadFile = File(...),
event_id: Optional[int] = Form(None),
description: Optional[str] = Form(""),
db: Session = Depends(get_db)
):
"""上传图片文件"""
try:
# 检查文件类型
if file.content_type not in ALLOWED_IMAGE_TYPES:
raise HTTPException(
status_code=400,
detail=f"不支持的文件类型: {file.content_type}"
)
# 检查文件大小
file_size = 0
content = await file.read()
file_size = len(content)
if file_size > MAX_IMAGE_SIZE * 1024 * 1024:
raise HTTPException(
status_code=400,
detail=f"文件大小超过限制: {MAX_IMAGE_SIZE}MB"
)
# 生成唯一文件名
file_id = str(uuid.uuid4())
file_extension = os.path.splitext(file.filename)[1]
filename = f"{file_id}{file_extension}"
file_path = os.path.join(IMAGE_DIR, filename)
# 保存文件
with open(file_path, "wb") as f:
f.write(content)
return {
"file_id": file_id,
"file_url": f"/uploads/images/{filename}",
"file_size": file_size,
"event_id": event_id,
"description": description,
"uploaded_at": datetime.now().isoformat()
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"上传图片失败: {str(e)}")
@router.get("/files", summary="获取文件列表")
async def get_files(
file_type: Optional[str] = Query(None, description="文件类型: video/image"),
page: int = Query(1, ge=1, description="页码"),
size: int = Query(20, ge=1, le=100, description="每页数量"),
db: Session = Depends(get_db)
):
"""获取上传文件列表"""
try:
# TODO: 实现真实的文件列表查询
# 当前返回模拟数据
files = [
{
"file_id": "video_123",
"file_url": "/uploads/videos/video_123.mp4",
"file_size": 1024000,
"file_type": "video",
"duration": 30.5,
"uploaded_at": "2024-01-15T10:30:00Z"
},
{
"file_id": "image_456",
"file_url": "/uploads/images/image_456.jpg",
"file_size": 256000,
"file_type": "image",
"uploaded_at": "2024-01-15T10:30:00Z"
}
]
# 过滤文件类型
if file_type:
files = [f for f in files if f["file_type"] == file_type]
total = len(files)
start = (page - 1) * size
end = start + size
paginated_files = files[start:end]
return {
"files": paginated_files,
"total": total,
"page": page,
"size": size
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"获取文件列表失败: {str(e)}")
@router.delete("/files/{file_id}", summary="删除文件")
async def delete_file(
file_id: str,
db: Session = Depends(get_db)
):
"""删除上传的文件"""
try:
# TODO: 实现真实的文件删除
# 当前返回模拟数据
return {
"file_id": file_id,
"message": "文件已删除"
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"删除文件失败: {str(e)}")
@router.get("/stats", summary="获取上传统计")
async def get_upload_stats(db: Session = Depends(get_db)):
"""获取文件上传统计"""
try:
# TODO: 实现真实的上传统计
# 当前返回模拟数据
return {
"total_files": 156,
"total_size": 1024000000, # 1GB
"video_count": 89,
"image_count": 67,
"today_uploads": 12
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"获取上传统计失败: {str(e)}")