Django模型FileField上传1GB大文件报错 分块上传及其他解决方案咨询
Django 1GB大文件上传报错解决方案
你遇到的RequestDataTooBig报错本质是Django默认DATA_UPLOAD_MAX_MEMORY_SIZE参数限制了单次请求体最大为2.5MB,整包上传1GB文件直接超过阈值触发拦截。以下是三类可行解决方案:
1. 临时快速解决方案(低并发、内部使用场景)
不需要修改上传逻辑,调整配置即可直接使用,适合仅在admin后台上传、使用频率低的场景:
- 在项目
settings.py中调整两个核心参数:
# 调整单次请求最大允许大小,此处设置为2GB,可按需调整 DATA_UPLOAD_MAX_MEMORY_SIZE = 2 * 1024 * 1024 * 1024 # 调整文件内存缓存阈值,超过该值的文件自动写入临时磁盘,避免占满服务器内存 FILE_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024 # 设为10MB
- 若使用Nginx等反向代理,需要同步调整代理层的请求大小限制,否则会被反向代理提前拦截,Nginx配置示例:
server { # 其他配置保持不变 client_max_body_size 2G; }
该方案缺点是不支持断点续传,上传过程中网络中断需要重新上传整个文件,大文件上传失败概率高,不适合面向普通用户的公共上传入口。
2. 分块上传实现方案(自研生产场景)
分块上传逻辑是前端将大文件切割为固定大小的小块(一般每块5~20MB),逐块上传到后端,后端全部接收完成后合并为完整文件关联到模型,支持断点续传、上传进度展示,适合高频大文件上传场景:
后端实现
首先新增分块上传临时记录表,存储上传的块信息:
from django.db import models import os from django.conf import settings class ChunkUpload(models.Model): file_md5 = models.CharField(max_length=32, verbose_name="文件唯一MD5", db_index=True) chunk_index = models.IntegerField(verbose_name="块序号") total_chunks = models.IntegerField(verbose_name="总块数") chunk_file = models.FileField(upload_to="chunk_temp/", verbose_name="临时块文件") filename = models.CharField(max_length=255, verbose_name="原始文件名") class Meta: unique_together = ('file_md5', 'chunk_index')
新增三个接口完成全流程:
- 秒传校验接口:前端上传前传递文件MD5和文件名,后端先校验是否已经存在上传完成的相同文件,存在直接返回文件地址无需重复上传
- 块上传接口:接收前端传递的块数据、MD5、块序号、总块数,存储到
ChunkUpload表 - 合并接口:前端全部块上传完成后调用,按序号合并所有块为完整文件,存储到你原有
save_support_file指定的路径,关联到Software模型的software_file字段后删除临时块数据
合并核心逻辑示例:
def merge_file_chunks(file_md5, total_chunks, filename): chunks = ChunkUpload.objects.filter(file_md5=file_md5).order_by('chunk_index') if chunks.count() != total_chunks: raise ValueError("上传块缺失,合并失败") # 调用原有路径生成逻辑获取保存路径 save_relative_path = save_support_file(Software(), filename) full_save_path = os.path.join(settings.MEDIA_ROOT, save_relative_path) # 合并文件 with open(full_save_path, 'wb') as full_file: for chunk in chunks: with open(chunk.chunk_file.path, 'rb') as chunk_f: full_file.write(chunk_f.read()) # 清理临时块 chunk.chunk_file.delete() chunk.delete() return save_relative_path
前端实现
前端借助SparkMD5计算文件唯一MD5,按固定大小切割文件逐块上传,示例逻辑:
const CHUNK_SIZE = 10 * 1024 * 1024 // 每块10MB const uploadFile = document.getElementById('file-input').files[0] const totalChunks = Math.ceil(uploadFile.size / CHUNK_SIZE) // 此处省略SparkMD5计算文件MD5的逻辑 // 逐块上传 for (let i = 0; i < totalChunks; i++) { const chunk = uploadFile.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE) const formData = new FormData() formData.append('chunk', chunk) formData.append('chunk_index', i) formData.append('total_chunks', totalChunks) formData.append('file_md5', 文件MD5值) formData.append('filename', uploadFile.name) // 调用块上传接口,可控制并发数避免请求过多 } // 全部上传完成后调用合并接口,获取完整文件路径后提交Software模型的其他字段(分类、关联产品等)
3. 对象存储直传方案(低维护生产场景)
该方案不需要自研分块上传逻辑,稳定性更高,适合生产环境高并发场景:
- 申请阿里云OSS、腾讯云COS等对象存储服务,所有服务商都已封装好大文件分块上传、断点续传能力
- 前端直接调用对象存储的JS SDK将文件上传到存储服务,上传完成后获取文件的公网访问地址
- 后端仅需要将该地址存储到
Software模型的software_file字段即可,不需要处理文件上传逻辑,也不会有请求大小限制问题
该方案优势是不需要占用服务器带宽和存储资源,维护成本极低,上传稳定性远高于自研分块上传。
内容的提问来源于stack exchange,提问作者Hüseyin Daş
相关产品推荐
相关产品推荐

