必须绕开表单提交,改用 file api + xmlhttprequest 或 fetch 手动分片上传,因为提交会一次性加载全部文件到内存导致崩溃;file.slice()是唯一可控分段入口,需配合合理分片大小、并发控制与content-range头实现断点续传。

直接用 <form></form> 提交大批量文件会触发浏览器一次性加载全部文件内容到内存,极易卡死或崩溃——这不是配置问题,是 HTML 表单本身的设计限制。必须绕开表单提交,改用 File API + XMLHttpRequest 或 fetch 手动分片上传。
为什么不能靠 enctype="multipart/form-data" 解决大批量上传
表单提交本质是同步阻塞式行为:浏览器会把所有选中的 File 对象读入内存、拼成 multipart boundary 数据、再发请求。哪怕只选 10 个 200MB 的视频,内存占用轻松突破 2GB,Chrome 直接弹出“Aw, Snap!”。它不支持流式读取,也不允许你控制读取节奏或释放中间资源。
-
input[type="file"]多选后,files是只读 FileList,但每个File实例仍持有完整文件引用 - 一旦调用
form.submit(),浏览器就接管了整个流程,你无法插入切片、校验、进度监听等逻辑 -
webkitdirectory等扩展属性仅影响选择范围,不改变提交机制本身
file.slice() 是唯一可控的分段入口
所有分片逻辑都得从这里开始。别试图用 readAsDataURL 或整文件 readAsArrayBuffer,那等于把炸弹提前引爆。
- 每片大小建议设为
5 * 1024 * 1024(5MB),太小导致 HTTP 开销占比过高,太大容易触发单次 FileReader 内存峰值 -
start和end必须是字节偏移量,不是字符数或行号;可用file.size动态算最后一片:Math.min(start + chunkSize, file.size) -
file.slice()返回新Blob,原File不受影响,但你不手动释放FileReader实例,多个并发读会堆积 GC 压力 - 读完立即设
reader = null,避免闭包持有 Blob 引用,否则 V8 不敢回收
并发上传必须用 Promise 队列而非 Promise.all
浏览器对同域并发 XMLHttpRequest 有硬限制(通常 6 个),盲目并发不仅没提速,反而因排队和重试放大失败率。
- 用
async/await+ 循环控制更稳妥,例如每次只启动不超过 3 个fetch请求 - 每个请求必须带明确标识:
filename、chunkIndex、totalChunks、content-range头(如bytes 0-5242879/10485759) - 服务端需按
content-range定位写入位置,否则多片并发写入会覆盖错位 - 失败时只重试当前片,不要全量回滚——这是断点续传的基础
真正麻烦的不是切片,而是服务端如何原子化合并、校验完整性、处理跨请求状态。前端能做的只是把每片干净、有序、可重入地送出去;一旦漏掉 content-range 或错用 multipart/form-data 包裹单片,后端就只能靠猜来拼接。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











