from fastapi import APIRouter, HTTPException, Depends, UploadFile, File, Form, Query from fastapi.responses import JSONResponse from pydantic import BaseModel import logging from typing import List, Optional, Dict, Any from app.services.qcloud_service import QCloudCOSService logger = logging.getLogger(__name__) router = APIRouter() class STSTokenResponse(BaseModel): """STS临时凭证响应""" credentials: Dict[str, str] expiration: str request_id: str bucket: str region: str class FileInfo(BaseModel): """文件信息""" key: str url: str size: int last_modified: str etag: str class ListFilesResponse(BaseModel): """列出文件响应""" files: List[FileInfo] is_truncated: bool next_marker: Optional[str] = None common_prefixes: Optional[List[Dict[str, Any]]] = None @router.post("/upload", tags=["腾讯云COS"]) async def upload_file( file: UploadFile = File(...), directory: str = Form("uploads"), qcloud_service: QCloudCOSService = Depends(lambda: QCloudCOSService()) ): """ 上传文件到腾讯云COS - **file**: 要上传的文件 - **directory**: 存储目录(默认为"uploads") """ try: # 读取文件内容 file_content = await file.read() # 上传文件 result = await qcloud_service.upload_file( file_content=file_content, file_name=file.filename, directory=directory, content_type=file.content_type ) return result except Exception as e: logger.error(f"文件上传失败: {str(e)}") raise HTTPException(status_code=500, detail=f"文件上传失败: {str(e)}") finally: # 关闭文件 await file.close() @router.delete("/files/{key:path}", tags=["腾讯云COS"]) async def delete_file( key: str, qcloud_service: QCloudCOSService = Depends(lambda: QCloudCOSService()) ): """ 从腾讯云COS删除文件 - **key**: 文件的对象键(COS路径) """ try: success = await qcloud_service.delete_file(key=key) if success: return {"message": "文件删除成功", "key": key} else: raise HTTPException(status_code=404, detail=f"文件删除失败,文件可能不存在") except Exception as e: logger.error(f"文件删除失败: {str(e)}") raise HTTPException(status_code=500, detail=f"文件删除失败: {str(e)}") @router.get("/files/url/{key:path}", tags=["腾讯云COS"]) async def get_file_url( key: str, expires: int = Query(3600, description="URL有效期(秒)"), qcloud_service: QCloudCOSService = Depends(lambda: QCloudCOSService()) ): """ 获取COS文件的临时访问URL - **key**: 文件的对象键(COS路径) - **expires**: URL的有效期(秒),默认3600秒 """ try: url = await qcloud_service.get_file_url(key=key, expires=expires) return {"url": url, "key": key, "expires": expires} except Exception as e: logger.error(f"获取文件URL失败: {str(e)}") raise HTTPException(status_code=500, detail=f"获取文件URL失败: {str(e)}") @router.post("/sts-token", response_model=STSTokenResponse, tags=["腾讯云COS"]) async def generate_sts_token( allow_prefix: str = Form("*", description="允许操作的对象前缀"), duration_seconds: int = Form(1800, description="凭证有效期(秒)"), qcloud_service: QCloudCOSService = Depends(lambda: QCloudCOSService()) ): """ 生成腾讯云COS临时访问凭证(STS),用于前端直传 - **allow_prefix**: 允许操作的对象前缀,默认为'*'表示所有对象 - **duration_seconds**: 凭证有效期(秒),默认1800秒 """ try: sts_result = await qcloud_service.generate_cos_sts_token( allow_prefix=allow_prefix, duration_seconds=duration_seconds ) # 添加桶和区域信息,方便前端使用 sts_result['bucket'] = qcloud_service.bucket sts_result['region'] = qcloud_service.region return sts_result except Exception as e: logger.error(f"生成STS临时凭证失败: {str(e)}") raise HTTPException(status_code=500, detail=f"生成STS临时凭证失败: {str(e)}") @router.get("/files", response_model=ListFilesResponse, tags=["腾讯云COS"]) async def list_files( directory: str = Query("", description="目录前缀"), limit: int = Query(100, description="返回的最大文件数"), marker: str = Query("", description="分页标记"), qcloud_service: QCloudCOSService = Depends(lambda: QCloudCOSService()) ): """ 列出COS中的文件 - **directory**: 目录前缀 - **limit**: 返回的最大文件数 - **marker**: 分页标记 """ try: result = await qcloud_service.list_files( directory=directory, limit=limit, marker=marker ) return result except Exception as e: logger.error(f"列出文件失败: {str(e)}") raise HTTPException(status_code=500, detail=f"列出文件失败: {str(e)}")