You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django REST API搭配React如何实现多视频文件批量上传

Django REST Framework + React 多视频拖放上传实现方案

多视频上传和多图片上传的核心逻辑完全一致,不存在场景适配差异,只需要调整模型结构、序列化规则、视图解析配置,再配合前端拖放表单逻辑即可实现,具体步骤如下:


第一步:调整数据模型

单个FileField只能存储单个文件,需要通过一对多关联拆分文件存储结构,不要把多个文件强行塞到同一个字段里:

from django.db import models
from django.core.validators import FileExtensionValidator

# 单条视频记录,存储每个视频的实际文件路径
class UploadedVideo(models.Model):
    file = models.FileField(
        upload_to='videos_uploaded',
        validators=[FileExtensionValidator(
            allowed_extensions=['mp4', 'm4v', 'mov', 'mpg', 'mpg2', 'mpeg']
        )]
    )
    collection = models.ForeignKey(
        'FileCollection',
        on_delete=models.CASCADE,
        related_name='files'
    )
    created_at = models.DateTimeField(auto_now_add=True)

# 文件集合模型
class FileCollection(models.Model):
    name = models.CharField(max_length=150, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

修改完模型后依次执行python manage.py makemigrations、python manage.py migrate完成数据库表更新。


第二步:配置DRF序列化器

通过嵌套序列化器支持多文件批量写入:

from rest_framework import serializers
from .models import FileCollection, UploadedVideo

class UploadedVideoSerializer(serializers.ModelSerializer):
    class Meta:
        model = UploadedVideo
        fields = ['id', 'file', 'created_at']

class FileCollectionSerializer(serializers.ModelSerializer):
    # 核心:指定many=True接收多文件列表
    files = UploadedVideoSerializer(many=True, required=True)

    class Meta:
        model = FileCollection
        fields = ['id', 'name', 'files', 'created_at']

    def create(self, validated_data):
        files_data = validated_data.pop('files')
        collection = FileCollection.objects.create(**validated_data)
        # 批量创建关联的视频记录
        for file_data in files_data:
            UploadedVideo.objects.create(collection=collection, **file_data)
        return collection

第三步:调整视图配置

DRF默认解析器无法识别multipart/form-data格式的多文件 payload,需要手动指定解析器,同时提取请求中的多文件列表传给序列化器:

from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework import viewsets
from .models import FileCollection
from .serializers import FileCollectionSerializer

class FileCollectionViewSet(viewsets.ModelViewSet):
    queryset = FileCollection.objects.prefetch_related('files').all()
    serializer_class = FileCollectionSerializer
    # 必须添加这两个解析器,才能正确解析表单上传的文件
    parser_classes = [MultiPartParser, FormParser]

    def perform_create(self, serializer):
        # 提取同名字段下的所有上传文件
        uploaded_files = self.request.FILES.getlist('files')
        serializer.save(
            name=self.request.data.get('name'),
            files=[{'file': f} for f in uploaded_files]
        )

第四步:React端拖放表单实现

拖放逻辑和图片上传完全一致,只需要注意FormData传参时把所有文件追加到同一个files字段下,大视频上传可按需调大请求超时时间:

import { useState } from 'react';
import axios from 'axios';

export default function VideoUploadForm() {
  const [dragActive, setDragActive] = useState(false);
  const [selectedFiles, setSelectedFiles] = useState([]);

  // 拖放事件统一处理,必须阻止默认行为避免浏览器直接打开文件
  const handleDragEvent = (e) => {
    e.preventDefault();
    e.stopPropagation();
    const eventType = e.type;
    if (eventType === 'dragenter' || eventType === 'dragover') {
      setDragActive(true);
    } else if (eventType === 'dragleave' || eventType === 'drop') {
      setDragActive(false);
    }
  };

  const handleFileDrop = (e) => {
    handleDragEvent(e);
    if (e.dataTransfer.files?.length) {
      setSelectedFiles(Array.from(e.dataTransfer.files));
    }
  };

  const handleFileSelect = (e) => {
    if (e.target.files?.length) {
      setSelectedFiles(Array.from(e.target.files));
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    const formData = new FormData();
    formData.append('name', '我的视频合集');
    // 所有文件追加到同名字段下
    selectedFiles.forEach(file => formData.append('files', file));

    try {
      const res = await axios.post('/api/file-collections/', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
        timeout: 300000 // 5分钟超时,适配大视频上传
      });
      console.log('上传成功', res.data);
      setSelectedFiles([]);
    } catch (err) {
      console.error('上传失败', err);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div
        onDragEnter={handleDragEvent}
        onDragOver={handleDragEvent}
        onDragLeave={handleDragEvent}
        onDrop={handleFileDrop}
        style={{
          border: dragActive ? '2px solid #1677ff' : '2px dashed #ccc',
          padding: '48px 24px',
          borderRadius: '8px',
          textAlign: 'center'
        }}
      >
        <input
          type="file"
          multiple
          accept=".mp4,.m4v,.mov,.mpg,.mpeg"
          onChange={handleFileSelect}
        />
        <p style={{marginTop: '12px'}}>
          {dragActive ? '松开鼠标上传文件' : '拖拽视频文件到此处,或点击选择本地文件'}
        </p>
        {selectedFiles.length > 0 && (
          <div style={{marginTop: '16px', textAlign: 'left'}}>
            <p>已选择 {selectedFiles.length} 个文件:</p>
            <ul>
              {selectedFiles.map((file, idx) => (
                <li key={idx}>
                  {file.name}({(file.size / 1024 / 1024).toFixed(2)}MB)
                </li>
              ))}
            </ul>
          </div>
        )}
      </div>
      <button
        type="submit"
        disabled={selectedFiles.length === 0}
        style={{marginTop: '16px', padding: '8px 24px'}}
      >
        开始上传
      </button>
    </form>
  );
}

额外配置注意事项

  • 视频文件普遍体积较大,需要在Django配置中调整上传大小限制:
    # settings.py 按需调整上限
    DATA_UPLOAD_MAX_MEMORY_SIZE = 5 * 1024 * 1024 * 1024 # 总上传上限5G
    FILE_UPLOAD_MAX_MEMORY_SIZE = 1 * 1024 * 1024 * 1024 # 单文件内存缓存上限1G
    
  • 如果服务端用Nginx反向代理,需要同步调整client_max_body_size配置,避免Nginx层拦截大文件请求
  • 前端可提前校验文件后缀、大小,减少无效请求
  • 若需要支持断点续传、分片上传,可在上述基础逻辑上扩展接口,基础批量上传功能上述代码可直接运行

内容的提问来源于stack exchange,提问作者Matthias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 14:57:21