HTML表单提交不支持分段上传,必须绕开form用File.slice()切片并手动构造请求,配合服务端分片合并逻辑才能实现可靠的大文件上传。
HTML表单提交(<form enctype="multipart/form-data">)根本不支持内存分段——它是一次性把整个文件塞进请求体的原子操作,前端无任何干预余地。想靠改表单属性或加 JS 钩子来“分段”,纯属方向错误。
enctype="multipart/form-data" 无法分段浏览器在提交该类型表单时,会将全部文件内容序列化为一个完整的 multipart boundary 流,中间不可中断、不可切片、不可注入元数据。你无法控制它何时发哪一段,也无法监听“已发 30%”或重试某一段。
RangeError: Maximum call stack size exceeded 或直接卡死标签页XMLHttpRequest.upload.onprogress 在表单提交中完全不生效,因为底层走的是同步导航流程,不是 XHRmultipart/form-data 请求体,没有 Content-Range、没有 chunkIndex,没法做断点校验或并行写入File.prototype.slice() 是唯一可行的切片入口真正能动手切的只有 File 对象本身,必须绕开表单,用 JS 主动调用 slice() 提取二进制片段,再逐片构造请求。
file.slice(0, 5 * 1024 * 1024) 才是 5MB,file.slice(0, 5000000) 虽数值对但易错,不推荐Math.min(start + chunkSize, file.size) 做 end 边界防护,否则最后一片可能越界报 InvalidStateError
Blob,不是引用;原 File 对象可长期持有,不用 URL.createObjectURL()——那玩意不手动 revoke 会持续占内存file.webkitSlice?.(start, end) 或 file.mozSlice?.(start, end),现代环境可直用 slice
Promise.all 直接扔全部分片1GB 文件切成 200 片,Promise.all(chunks.map(upload)) 会瞬间发起 200 个请求,远超浏览器同域并发上限(Chrome 默认 6),结果是大量连接被挂起、内存暴涨、页面无响应。
立即学习“前端免费学习笔记(深入)”;
Promise.allSettled + 固定长度队列(如每次最多发 4 个),完成一个再补一个XMLHttpRequest 实例,上传完立即设为 null,避免实例堆积Content-Range,例如 bytes 0-5242879/1073741824,服务端靠它定位写入位置new FormData(),再 .append('file', blob),否则 blob 引用可能错乱前端切得再准、传得再稳,如果服务端收完所有片就扔进 /tmp 然后不管,或者合并时没按 chunkIndex 排序、没做哈希校验,最终文件一定损坏。这是最常被跳过的协同环节。
/upload/status?uploadId=xxx 接口,返回已接收的 chunkIndex 列表uploadId 下所有临时文件,按 chunkIndex 升序拼接,最后计算整体 md5 或 sha256 校验{"code": 4001, "msg": "chunk 5 missing"},不能只抛 500uploadId 必须基于文件指纹(如 Web Crypto API 的 digest('SHA-256', buffer)),不能用 Math.random() 或时间戳——同名文件反复上传会覆盖进度真正的分段上传不是前端切几刀就完了,而是前端切、传、重试、查进度,服务端收、存、校、合、验,两端协议对齐了,才算落地。漏掉任意一环,用户点上传按钮那一刻,就已经注定失败。