formdata传大文件需手动分片上传,用file.slice()切2–5mb/片,每片带chunkindex、totalchunks、uploadid,服务端须支持任意顺序接收、幂等写入、断点查询及校验合并。

FormData 传大文件会卡死或失败,因为没分片
浏览器原生 FormData 不处理分片,直接塞进内存上传,超 100MB 就容易触发内存警告、请求超时或 Network Error。服务端也可能拒绝未分片的巨量 payload。
真正能落地的做法是:手动切片 + 每片单独发 FormData 请求,靠唯一 uploadId 关联同一文件。
- 用
File.prototype.slice()(非ArrayBuffer.slice)切,兼容性好,返回新Blob - 每片加字段:
chunkIndex、totalChunks、uploadId(前端生成crypto.randomUUID()或时间戳+随机数) - 不要等所有片发完再合并——服务端应支持「任意顺序接收」,靠
uploadId+chunkIndex写临时文件
断点续传必须依赖服务端校验,前端只管重试逻辑
前端无法知道某片是否真被服务端落盘成功,只看 http 状态码不保险(比如 nginx 缓存了 200 但后端写失败)。断点能力本质在服务端是否提供 /upload/status?uploadId=xxx 接口。
- 上传前先 GET
/upload/status?uploadId=xxx,返回已上传的chunkIndex列表 - 跳过已存在的片,只发缺失的;注意服务端需对每个
chunkIndex做幂等写入(如先检查文件是否存在) - 别用
XMLHttpRequest.upload.onprogress估算进度——它反映的是浏览器发出数据的速度,不是服务端接收完成时间
chrome/firefox 对单个 FormData 的 Blob 大小没硬限制,但 V8 内存会爆
FormData.append('file', file.slice(0, 5 * 1024 * 1024)) 这种操作本身不会报错,但若切片太大(比如 50MB/片),js 引擎在构造 FormData 时可能把整个 Blob 加载进内存,触发 Chrome 的 RangeError: Maximum call stack size exceeded 或直接崩溃。
立即学习“前端免费学习笔记(深入)”;
- 安全切片大小建议:2–5MB/片(实测 Firefox 对 10MB/片开始明显卡顿)
- 用
file.slice(start, end)而不是读成ArrayBuffer再转Blob,避免双份内存占用 - 上传中保持对
File对象的弱引用即可,不用URL.createObjectURL()——那玩意不释放会吃内存
服务端没实现分片合并,前端再努力也没用
很多前端同学花半天写分片逻辑,结果发现服务端收完所有片就扔了,或者合并时顺序错乱、校验缺失,最终文件损坏。这是最常被忽略的协同点。
- 确认服务端是否支持按
uploadId扫描临时目录、按chunkIndex排序拼接、最后做md5或sha256校验 - 要求服务端返回明确错误:比如
{"code": 4001, "msg": "chunk 5 missing"},而不是笼统的 500 - 前端重试最多 3 次,第 4 次该报错就报错,别无脑循环——可能是服务端根本没监听那个
uploadId
分片和断点续传不是前端单方面能闭环的事,服务端接口设计决定了 80% 的成败。别在没对齐 API 合约前写一堆切片工具函数。