DRF多个文件上传

674 阅读1分钟

场景

从前端同时上传将多个文件发送到后端API,文件进行大小和后缀名验证。

View

DRF没有支持多文件上传的View,所以需要自定义一个。 这里使用APIView进行继承。

from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.views import APIView
from rest_framework import status
from .serializers import MultiFileUploadSerializer

class MultiFileUploadView(APIView):
    parser_classes = (FormParser, MultiPartParser)

    def post(self, request):
        fs = MultiFileUploadSerializer(data=request.data)
        fs.is_valid(raise_exception=True)
        validated_data = fs.validated_data
        # 这里可以得到验证后的上传文件
        validated_data.get("files")
        return Response(status=status.HTTP_200_OK)

Serializer

上面的MultiFileUploadView中使用了MultiFileUploadSerializer,在这里进行定义。 通过ListFieldchild字段,Serializer可以接收多个文件。 通过validate_files方法验证文件的大小和后缀名。

from rest_framework import serializers

# 单个文件大小上限
SINGLE_FILE_SIZE_LIMIT = 104857600 #100 * 1024 * 1024
# 所有文件大小上限
TOTAL_FILE_SIZE_LIMIT = 104857600 #100 * 1024 * 1024
# 允许使用的后缀名
ALLOWED_FILE_EXTENSIONS = ["png", "mp4"]


class MultiFileUploadSerializer(serializers.Serializer):
    files = serializers.ListField(
        child=serializers.FileField(required=True, max_length=SINGLE_FILE_SIZE_LIMIT, allow_empty_file=False)
    )

    def validate_files(self, data):
        total_size = 0
        # 检查后缀名
        for f in data:
            extension = f.name.split(".")[-1]
            if extension not in ALLOWED_FILE_EXTENSIONS:
                raise serializers.ValidationError("无法上传此类文件")
            total_size += f.size
        # 检查文件大小上限
        if total_size > TOTAL_FILE_SIZE_LIMIT:
            raise serializers.ValidationError("文件超过100M")

        return data

对上传后的文件进行操作

在上面的View中,通过.validated_data.get("files")可以取得验证后的文件列表。 循环文件列表,发现上传后的文件可能是TemporaryUploadedFileInMemoryUploadedFile类型。对上传的文件可以进行一些操作。

for file in files:
    # 取得文件名
    file.name
    # 取得文件大小
    file.size
    # 取得content type
    file.content_type
    # 读取文件
    file.file.read()

URL

from django.urls import path
from . import views

urlpatterns = [
    path("multi-upload/", views.MultiFileUploadView.as_view(), name="multi-file-upload-view")
]