from rest_framework.decorators import api_view, parser_classes, authentication_classes, permission_classes from rest_framework.response import Response from rest_framework import status from rest_framework.parsers import MultiPartParser, FormParser from rest_framework.permissions import IsAuthenticated from django.views.decorators.csrf import csrf_exempt from .oss import OSSUploader from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi import logging import traceback import os from userapp.authentication import RedisTokenAuthentication logger = logging.getLogger(__name__) @swagger_auto_schema( method='post', operation_description="通用文件上传接口,上传到OSS", manual_parameters=[ openapi.Parameter( 'file', openapi.IN_FORM, description="需要上传的文件", type=openapi.TYPE_FILE, required=True ), openapi.Parameter( 'is_permanent', openapi.IN_FORM, description="是否永久保存文件,默认为临时文件", type=openapi.TYPE_BOOLEAN, default=False ), openapi.Parameter( 'filename', openapi.IN_FORM, description="自定义文件名(可选),如果不提供则自动生成", type=openapi.TYPE_STRING, required=False ), ], responses={ 200: openapi.Response( description="上传成功", schema=openapi.Schema( type=openapi.TYPE_OBJECT, properties={ 'url': openapi.Schema(type=openapi.TYPE_STRING, description="文件访问URL"), 'key': openapi.Schema(type=openapi.TYPE_STRING, description="OSS对象键名"), 'is_permanent': openapi.Schema(type=openapi.TYPE_BOOLEAN, description="是否永久文件") } ) ), 400: openapi.Response(description="请求错误或上传失败"), 401: openapi.Response(description="认证失败") }, tags=['文件上传'], security=[{'Bearer': []}] ) @csrf_exempt @api_view(['POST']) @parser_classes([MultiPartParser, FormParser]) @authentication_classes([RedisTokenAuthentication]) @permission_classes([IsAuthenticated]) def upload_file(request): """ 通用文件上传接口,将文件上传到OSS 文件保存在 upload/{temporary|permanent}/{date}/{filename} 结构下 需要提供有效的认证令牌 """ try: # 检查是否有文件 if 'file' not in request.FILES: print("[Upload] No file in request.FILES, keys:", list(request.FILES.keys())) return Response({"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST) file_obj = request.FILES['file'] original_name = file_obj.name file_size = file_obj.size content_type = getattr(file_obj, 'content_type', 'unknown') print(f"[Upload] File received: {original_name}, size={file_size}, type={content_type}") # 获取参数 is_permanent_raw = request.data.get('is_permanent', 'false') is_permanent = str(is_permanent_raw).lower() == 'true' filename = request.data.get('filename', None) # 上传文件 uploader = OSSUploader() result = uploader.upload_file(file_obj, is_permanent, filename) # 返回包含前端需要的字段 _, ext = os.path.splitext(original_name) response_data = { 'url': result['url'], 'key': result['key'], 'is_permanent': result['is_permanent'], 'filename': original_name, 'size': file_size, 'mimeType': content_type, } print(f"[Upload] Success: {result['url']}") return Response(response_data, status=status.HTTP_200_OK) except Exception as e: error_detail = traceback.format_exc() print(f"[Upload] ERROR: {error_detail}") logger.error(f"File upload failed: {str(e)}\n{error_detail}") return Response( {"error": "File upload failed", "detail": str(e)}, status=status.HTTP_400_BAD_REQUEST )