eagleye

通过 Axios 与 DRF 实现带优化标志的头像上传完整指南

通过 Axios 与 DRF 实现带优化标志的头像上传完整指南

一、前后端交互核心流程

核心目标:前端通过FormData同时传递文件与optimize_avatar控制标志,后端 DRF 接收并根据标志执行图像优化逻辑。

数据流向

graph LR

A[前端表单] -->|FormData| B(Axios 上传请求)

B -->|multipart/form-data| C[DRF 视图]

C --> D[序列化器验证]

D -->|提取 optimize_avatar| E{是否优化}

E -- 是 --> F[图像优化处理]

E -- 否 --> G[直接保存]

F --> G

G --> H[返回结果]

二、前端实现(Axios)

1. 基础表单构建

// 构建 FormData(文件 + 优化标志)

const formData = new FormData();

formData.append('avatar', fileInput.files[0]); // 文件对象

formData.append('optimize_avatar', 'true'); // 优化标志(字符串/布尔值均可)

2. Axios 请求配置

axios.post('/api/avatar/upload/', formData, {

headers: {

'Content-Type': 'multipart/form-data', // 必须:文件上传格式

'X-CSRFToken': getCookie('csrftoken') // Django CSRF 保护

},

// 可选:上传进度监控

onUploadProgress: progressEvent => {

const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);

console.log(`上传进度: ${percent}%`);

}

})

.then(response => console.log('上传成功:', response.data))

.catch(error => console.error('上传失败:', error.response.data));

3. 高级参数传递(可选)

// 传递详细优化参数(如质量、尺寸)

formData.append('optimize_options', JSON.stringify({

quality: 80, // 压缩质量(0-100)

maxWidth: 1024, // 最大宽度

format: 'webp' // 目标格式

}));

三、后端实现(Django REST Framework)

1. 序列化器设计

from rest_framework import serializers

class AvatarUploadSerializer(serializers.Serializer):

avatar = serializers.ImageField() # 文件字段

optimize_avatar = serializers.BooleanField(

required=False,

default=False,

help_text="是否开启图像优化"

)

optimize_options = serializers.JSONField( # 高级优化参数(可选)

required=False,

default=None,

validators=[lambda v: v and set(v.keys()).issubset({'quality', 'maxWidth', 'format'})]

)

def validate_optimize_avatar(self, value):

"""权限验证示例:仅VIP用户可优化"""

if value and not self.context['request'].user.is_vip:

raise serializers.ValidationError("仅VIP用户支持图像优化")

return value

2. 视图处理逻辑

from rest_framework.views import APIView

from rest_framework.parsers import MultiPartParser

from rest_framework.response import Response

from PIL import Image

from io import BytesIO

from django.core.files.uploadedfile import InMemoryUploadedFile

class AvatarUploadView(APIView):

parser_classes = [MultiPartParser] # 关键:支持文件上传

def post(self, request):

serializer = AvatarUploadSerializer(

data=request.data,

context={'request': request}

)

if not serializer.is_valid():

return Response(serializer.errors, status=400)

# 提取参数

avatar_file = serializer.validated_data['avatar']

optimize = serializer.validated_data.get('optimize_avatar', False)

options = serializer.validated_data.get('optimize_options', {})

# 优化逻辑

if optimize:

avatar_file = self._optimize_image(avatar_file, options)

# 保存文件(用户模型示例)

request.user.avatar.save(avatar_file.name, avatar_file)

return Response({

'status': 'success',

'avatar_url': request.user.avatar.url

})

def _optimize_image(self, file, options):

"""企业级图像优化核心逻辑"""

img = Image.open(file)

original_size = file.size

# 1. 尺寸调整(默认最大800px)

max_width = options.get('maxWidth', 800)

if img.width > max_width:

ratio = max_width / img.width

img = img.resize((max_width, int(img.height * ratio)), Image.Resampling.LANCZOS)

# 2. 格式转换与压缩

output = BytesIO()

format = options.get('format', 'webp').upper()

quality = options.get('quality', 85)

img.save(output, format=format, quality=quality, optimize=True)

# 3. 构建新内存文件对象

return InMemoryUploadedFile(

file=output,

field_name=file.field_name,

name=f"opt_{file.name}",

content_type=f"image/{format.lower()}",

size=output.tell(),

charset=None

)

四、企业级最佳实践

1. 安全增强
  • CSRF 保护:前端必须传递X-CSRFToken,后端可通过@ensure_csrf_cookie装饰器确保 Cookie 存在。
  • 文件类型白名单# 序列化器中验证文件类型

def validate_avatar(self, file):

if not file.content_type.startswith('image/'):

raise serializers.ValidationError("仅支持图像文件")

return file

  • 病毒扫描:集成ClamAV扫描上传文件:from clamd import ClamdUnixSocket

def scan_file(file):

clam = ClamdUnixSocket()

result = clam.instream(file.file)

if result['stream'][0] != 'OK':

raise ValidationError("文件安全检测失败")

2. 性能与容错
  • Nginx 限制上传大小client_max_body_size 20M; # 限制单文件最大20MB
  • 内存优化:大文件自动切换磁盘处理(依赖 DjangoFILE_UPLOAD_MAX_MEMORY_SIZE配置)。
  • 错误回退:优化失败时返回原始文件:try:

return self._optimize_image(file, options)

except Exception as e:

logger.error(f"优化失败: {str(e)}")

return file # 回退到原始文件

3. 监控与日志

# 记录优化效果指标

logger.info(

f"AVATAR_OPT: user={user.id} "

f"original={original_size/1024:.1f}KB "

f"optimized={optimized_size/1024:.1f}KB "

f"saved={(1-optimized_size/original_size)*100:.1f}%"

)

五、高级场景扩展

1. 多条件优化参数

前端传递 JSON 格式高级选项:

formData.append('optimize_options', JSON.stringify({

quality: 75, // 压缩质量

maxWidth: 600, // 最大宽度

format: 'jpeg', // 目标格式

grayscale: false // 是否转为灰度图

}));

2. 响应格式标准化
  • 成功响应{

"status": "success",

"avatar_url": "/media/avatars/opt_user123.webp",

"metrics": {

"original_size": 2456789,

"optimized_size": 567890,

"saved_percentage": 77

}

}

  • 错误响应{

"status": "error",

"code": "optimization_failed",

"message": "不支持的图像格式",

"fallback_url": "/media/avatars/user123_original.png"

}

六、关键总结

1. 字段名一致性:前端optimize_avatar必须与 DRF 序列化器字段名完全匹配。

2. 解析器配置DRF 视图需显式添加MultiPartParser以支持文件上传。

3. 企业级核心:安全验证(权限/病毒扫描)、异常处理(失败回退)、性能监控(优化指标)三位一体。

此方案可无缝扩展至其他文件处理场景(如文档转换、视频压缩),仅需调整序列化器字段与业务逻辑。

 

posted on 2025-07-24 17:13  GoGrid  阅读(15)  评论(0)    收藏  举报

导航