multiple属性仅支持多文件选择,不提供分片、并发或重试功能;分片必须用file.slice()手动实现,避免filereader预加载导致内存溢出;并发上传需限流控制,每片独立重试并携带content-range头;formdata多文件上传须用相同key多次append,否则后端无法正确解析。

multiple属性本身不参与分片或并发控制
加了 multiple 只是让 <input type="file"> 能选多个文件,它不切片、不并发、不排队、不带重试逻辑。所有调度行为必须由 JavaScript 手动实现。很多人误以为“用了 multiple 就能自动分片上传”,结果在 2GB 视频上传时页面卡死、内存暴涨——那是因为直接调了 form.submit() 或把整个 FileList 塞进 FormData,触发了浏览器原生的全量内存加载。
分片起点只能是 file.slice(),不是 FileReader 或 base64
真正可控的切片入口只有 file.slice(start, end):它返回新 Blob,不读取内容,也不占内存。别用 readAsDataURL 或 readAsArrayBuffer 预加载整文件——那等于提前把几百 MB 搬进 JS 堆,GC 来不及回收就 OOM。
-
start和end是字节偏移量,不是字符数;最后一片要动态算:Math.min(start + chunkSize, file.size) - 每次
slice()后,记得手动释放FileReader实例:reader = null,否则闭包持有Blob引用,V8 不敢回收 - 并发读取多个分片时,
FileReader实例必须隔离,不能复用同一个实例
并发上传必须限流,Promise.all 是陷阱
浏览器对同域 XMLHttpRequest 或 fetch 有硬性并发上限(通常 6 个),盲目用 Promise.all(chunks.map(upload)) 会导致大量请求排队、超时、重试雪崩。实际应走串行+滑动窗口控制。
- 推荐用
async/await循环,每次最多启动 3 个fetch请求 - 每个请求必须带唯一标识:
filename、chunkIndex、totalChunks - 必须设置
Content-Range头,例如:bytes 0-5242879/10485759,服务端靠它定位写入位置 - 失败只重试当前片,不要回滚整文件——断点续传的前提是每片可独立重入
FormData.append 的 key 写法决定后端能否收到全部文件
前端传多文件,后端收不到或只收到一个,90% 是 FormData.append() 的 key 用错了。这不是浏览器兼容问题,是协议层语义错误。
- ✅ 正确:对每个文件都用相同 key,如
formData.append('files', file),后端启用多值解析(Express 用upload.array('files'),PHP 遍历$_FILES['files']['name']) - ❌ 错误:
formData.append('files', input.files)—— 把整个FileList当一个值塞进去,行为不可控 - 别手动设
Content-Type;浏览器会自动生成multipart/form-databoundary,设错反而破坏结构 - 调试时盯紧 Network 面板的 Request Payload,确认每个文件是否作为独立 part 出现
Content-Range,或用 multipart/form-data 包单个分片,后端就只能靠猜拼接——这种细节,上线前不压测根本暴露不出来。











