分片上传不能用<form enctype="multipart/form-data">,因其原子性打包整文件,导致后端无法识别“第3片”;必须用JavaScript手动切片、独立发送每片,并通过唯一uploadId与chunkIndex实现断点状态持久化。

分片上传不能塞进 <form> 标签里,前端结构必须剥离表单语义,只保留选择和触发能力;持久化断点状态的关键是 uploadId 与 chunkIndex 的生成逻辑和存储位置,不是靠 localStorage 存个进度条数字。
为什么不能用 <form enctype="multipart/form-data"> 做分片
浏览器对 enctype="multipart/form-data" 的处理是原子性的:整个文件被打包成一个请求体,用 boundary 分隔字段,服务端靠 $_FILES 或等效机制解析。它不支持“第 3 片”这种概念——你传个叫 chunk_3 的字段,后端收到的只是个普通文本字段,不是原始二进制分片。
常见错误现象包括:
- 后端收到的
$_FILES['chunk_3']为空或类型错误 - Nginx 返回
413 Request Entity Too Large(因为整文件被当做一个 part 提交) - 刷新页面后所有进度丢失,服务端无任何中间记录
File.slice() 切片时的边界计算陷阱
切片起点和终点必须严格对齐字节偏移,否则最后一片会截断或越界。错误写法如 Math.floor(file.size / chunkSize) 在浮点误差下会导致 i * chunkSize < file.size 不成立,漏掉最后一块。
立即学习“前端免费学习笔记(深入)”;
正确做法是用 while 循环或 for 条件动态判断:
- 起始位置:
start = i * chunkSize - 结束位置:
end = Math.min(start + chunkSize, file.size) - 切片调用:
file.slice(start, end) - 确保
chunkIndex从0开始,且与后端合并逻辑一致
uploadId 和断点状态怎么存才可靠
uploadId 不是随便生成的 ID,它是客户端和服务端识别同一文件上传会话的唯一锚点。错用 Date.now() 或文件名会导致并发上传同名文件时状态混淆。
断点状态不能只存在内存里:
- 必须为每个文件生成独立
uploadId,推荐crypto.randomUUID()或uuidv4() - 已上传分片索引(
chunkIndex列表)应存在localStorage或 IndexedDB,键名为upload:${uploadId} - 每次上传前先发
/check-uploaded请求校验服务端实际完成状态,避免客户端缓存脏数据 - 用户关闭页面再打开时,靠
uploadId拉取服务端最新分片记录,而非直接读本地缓存
最容易被忽略的一点:uploadId 必须在第一次切片前就确定并持久化,而不是每传一片都重新生成;chunkIndex 的连续性由前端循环控制,但服务端必须能根据 uploadId + chunkIndex 精确定位到对应临时文件,缺一不可。



















