Django怎么上传大型文件_Python通过流式处理实现断点续传
Django默认文件上传失败因request.FILES将整个文件载入内存或临时文件,遇大文件易触发MemoryError、RequestEntityTooLarge等错误;其设计不支持大文件、无断点续传能力,需流式接收或前后端协同实现分片上传。
为什么直接用 Django 默认文件上传会失败
Django 的 request.FILES 默认把整个上传内容读进内存或临时文件,遇到几百 MB 甚至 GB 级文件时,容易触发 MemoryError、RequestEntityTooLarge(Nginx)、Connection reset(超时),或者被 WSGI 服务器(如 Gunicorn)强制中断。这不是代码写得不对,而是设计上就不适合大文件——它假设上传是“短小、完整、可信”的。
- Django 本身不提供断点续传逻辑,也不管理分片、校验、合并
-
FileUploadHandler可以干预上传流,但仅限于“接收过程”,无法暴露底层 socket 或恢复上传状态 - 所有“断点续传”必须由前端配合后端自定义接口实现,Django 只负责收、存、校验、拼接
如何用流式接收避免内存爆炸
核心是绕过 request.FILES,直接从 request.body 读取原始字节流,并边读边写入磁盘(或对象存储)。适用于单文件上传(非分片),但能撑住几 GB。
- 必须在视图中设置
request.upload_handlers = [],否则 Django 仍会尝试解析 multipart - 使用
iter(request.body.readline, b'')或request.body.read(chunk_size)分块读取,chunk_size建议设为8192或65536 - 写入时用
open(..., 'ab')追加模式,避免重复打开文件消耗句柄 - 示例片段:
def upload_stream_view(request): if request.method != 'POST': return JsonResponse({'error': 'only POST'}) request.upload_handlers = [] # 关键:禁用默认处理器 file_path = '/tmp/upload.bin' with open(file_path, 'ab') as f: for chunk in iter(lambda: request.body.read(8192), b''): f.write(chunk) return JsonResponse({'received': os.path.getsize(file_path)})
注意:request.body 是一次性流,读完即空;不能和 request.POST 或 request.FILES 混用。
Uberduck
开源的AI语音社区,拥有5000+电影动漫声库,适合做同人配音
下载
断点续传必须前后端协同设计
真正的断点续传不是“Django 支持与否”的问题,而是要约定一套状态协议。常见做法是前端按固定大小切片(如 5MB/片),每片带唯一 upload_id 和 chunk_index,后端记录已收哪些片。
- 后端需提供三个接口:
/upload/init/(创建上传会话,返回upload_id)、/upload/chunk/(接收并落盘单片)、/upload/merge/(校验 MD5、按序合并) - 已上传分片状态建议存在 Redis 或数据库里,字段至少包括:
upload_id、total_chunks、received_chunks(set 或 bitmap) - 合并时务必校验每片的
Content-MD5或前端传来的 SHA256,防止网络错包 - Nginx 需调大:
client_max_body_size、client_body_timeout、proxy_read_timeout,否则单片上传就超时
容易被忽略的部署细节
- Gunicorn 默认
--timeout 是 30 秒,大文件上传必然超时,必须调高(如 --timeout 600)
- 如果用 Nginx + Gunicorn,Nginx 的
proxy_buffering off 和 proxy_request_buffering off 要开,否则 Nginx 会缓存整个请求体再转发,失去流式意义
- 开发时用
runserver 测试流式上传不可靠——它不支持长连接和大 body,务必在真实部署环境验证
- 文件名、MIME 类型等元信息无法从
request.body 获取,必须由前端通过 URL 参数或 JSON body 单独传,比如 ?filename=report.zip&content_type=application/zip
--timeout 是 30 秒,大文件上传必然超时,必须调高(如 --timeout 600)proxy_buffering off 和 proxy_request_buffering off 要开,否则 Nginx 会缓存整个请求体再转发,失去流式意义runserver 测试流式上传不可靠——它不支持长连接和大 body,务必在真实部署环境验证request.body 获取,必须由前端通过 URL 参数或 JSON body 单独传,比如 ?filename=report.zip&content_type=application/zip
断点续传真正难的不是“怎么收”,而是“怎么管状态、怎么防并发冲突、怎么清理碎片、怎么应对客户端反复重试”。Django 只是管道,别指望它自动帮你记住哪片收到了。