因为request.files会将整个文件加载进内存,导致内存溢出或被nginx/wsgi拦截;应禁用自动解析,改用request.stream流式读取,并配合multipartparser(max_form_memory_size=0)安全解析分块上传。

为什么直接用 request.files 上传大文件会失败
因为 Flask 默认将整个请求体读入内存,遇到几百 MB 甚至 GB 级文件时,会触发 MemoryError 或被 Nginx/WSGI(如 Gunicorn)主动 kill。更关键的是,HTTP 协议本身不支持“暂停上传”,所谓“断点续传”必须由前端分片 + 后端按块存储 + 前端记录已传 offset 共同实现,Flask 只负责接收、校验、拼接。
request.stream 是唯一可行的底层入口
它返回一个可迭代的二进制流,不缓存全文,适合边收边写。但注意:一旦你调用 request.files 或 request.form,Flask 就会强制消费整个 stream,导致后续读取为空 —— 所以二者不可共存。
- 必须用
request.headers.get("Content-Type")手动解析 multipart boundary,或让前端改用application/octet-stream发送纯二进制分片 - 推荐前端用
fetch+Blob.slice()分片,每片带固定参数:filename、chunkIndex、totalChunks、identifier(如文件 hash) - 后端收到后,立即写入临时目录,路径建议为
f"/tmp/uploads/{identifier}/{chunkIndex}",避免并发覆盖
如何安全合并分片并防止重复提交
合并不是简单按序 cat,重点在原子性和幂等性。用户可能重发同一片(网络抖动),也可能多次触发“上传完成”请求。
- 用
os.listdir()检查/tmp/uploads/{identifier}/下是否已有全部0到totalChunks-1的文件,而不是依赖计数器 - 合并前先对每个分片做
sha256校验(前端应传每片 hash),匹配失败则返回 400 - 合并操作必须加文件锁(
fcntl.flock)或用 Redis setnx 控制,否则并发完成请求会导致重复拼接 - 拼接完成后,把最终文件移到永久路径,并清空临时目录 —— 这步失败要能回滚,至少保留原始分片
Nginx 配置不配好,再好的 Flask 逻辑也白搭
默认 Nginx 会拦截 >1MB 的请求体,并关闭长连接,导致分片上传中途断连。
- 必须在
http或server块中加:client_max_body_size 2048m(根据实际调) - 加
proxy_buffering off和proxy_request_buffering off,否则 Nginx 会缓存整个分片再转发给 Flask - 上传过程可能超时,设
proxy_read_timeout 600(10 分钟),并确保uwsgi_read_timeout或gunicorn --timeout同步调整
最易忽略的是:分片上传本质是多个独立 HTTP 请求,每个都要过一遍 Nginx → WSGI → Flask,所以 identifier 的一致性、临时目录清理的健壮性、以及磁盘空间监控,比合并逻辑本身更决定系统能否长期跑稳。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











