- Update food, outfits, props, home-decor pages and components - Add permissions page and sidebar updates - Update API client and all API modules (auth, food, dances, etc.) - Add card model migrations for optional fields - Update Django views, serializers, and authentication - Add affinity level migrations and user app updates - Add project documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
114 lines
4.3 KiB
Python
114 lines
4.3 KiB
Python
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
|
||
) |