分片上传需前后端严格对齐:前端传chunkIndex/totalChunks/identifier,后端禁用FILE_UPLOAD_MAX_MEMORY_SIZE、读request.body存为.part文件,合并前校验块完整性并追加写入,完成后校验hash并清理临时目录,nginx需配置超大body和超时。

前端分片上传时,File.slice() 的起始偏移量必须对齐后端预期
常见错误是前端用 file.slice(i * chunkSize, (i + 1) * chunkSize) 分片,但后端合并时没校验块序号或偏移量,导致文件错位、损坏。Django 默认不校验上传块的逻辑顺序,必须靠业务层约束。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 前端上传每个分片时,必须携带
chunkIndex(从 0 开始)、totalChunks、identifier(如文件名+大小+mtime 的 hash),不能只传序号 - 后端用
identifier作为临时目录名,把每个块存为{chunkIndex}.part,避免不同用户同名文件冲突 - 不要依赖 HTTP 请求顺序——HTTP/2 或并发上传下,
chunkIndex=5的请求可能比chunkIndex=3先到
Django 视图接收分片需禁用 FILE_UPLOAD_MAX_MEMORY_SIZE 并手动读取原始 body
Django 默认会把上传内容先缓存进内存或临时文件,但分片上传的单个请求体是二进制块,不是完整 multipart 表单;若走标准 request.FILES 流程,会触发 MultiValueDictKeyError 或解析失败。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 在视图中直接读
request.body,而非request.FILES:def upload_chunk(request): if request.method == 'POST': chunk_data = request.body # 原始字节流 identifier = request.POST.get('identifier') chunk_index = int(request.POST.get('chunkIndex', 0)) # ……保存到 media/chunks/{identifier}/{chunk_index}.part - 在
settings.py中设FILE_UPLOAD_MAX_MEMORY_SIZE = 0,强制所有上传走磁盘,避免大块数据撑爆内存 - 用
request.content_type校验是否为application/octet-stream,防止伪造请求
合并分片时,用 os.path.join() 拼接路径并校验块完整性
合并前不检查缺失块或损坏块,会导致最终文件不可用。Django 不提供内置分片校验机制,必须手动实现。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 合并前遍历
range(totalChunks),确认每个{i}.part文件存在且 size > 0;缺块则返回400 Bad Request并带缺失索引 - 每个块写入最终文件时,用
open(..., 'ab')追加模式,不要用'wb'覆盖——否则前面块全丢 - 合并完成后,用
hashlib.sha256()对最终文件重算 hash,与前端传来的fileHash对比,不一致则删掉并报错 - 临时分片目录(如
media/chunks/xxx/)必须在合并成功后立即shutil.rmtree(),否则磁盘被占满
生产环境必须加 nginx 代理层处理超大 body 和超时
Django 自身无法可靠处理几百 MB 以上的单次请求体;不配反向代理的话,容易触发 Connection reset by peer 或 Request Entity Too Large 错误。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 在
nginx.conf中设置:client_max_body_size 2G; client_body_timeout 300; proxy_read_timeout 300; proxy_send_timeout 300;
- 禁用
client_body_in_file_only on(除非你真需要落盘到 nginx 临时区),默认off即可 - 如果用
gunicorn,确保--timeout 300且--keep-alive 5,避免 worker 在合并中途被杀
分片上传真正的复杂点不在“怎么拼”,而在于“怎么兜底”:网络中断后前端如何知道该从哪块续、后端如何识别重复上传、失败后怎么清理残留。这些逻辑一旦漏掉一环,用户就会卡在 99%。
















