通过 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. 企业级核心:安全验证(权限/病毒扫描)、异常处理(失败回退)、性能监控(优化指标)三位一体。
此方案可无缝扩展至其他文件处理场景(如文档转换、视频压缩),仅需调整序列化器字段与业务逻辑。