必须用分片+fetch手动上传大文件:因原生form提交会将整个文件读入内存导致崩溃,无法控制进度/中断/重试;需file.slice()分片、设content-range头、并发限3、服务端原子合并。

原生 <form> 提交无法安全、可靠地上传大文件,必须放弃同步提交模式,改用分片 + 手动请求方式;否则浏览器内存爆满、服务端直接 413、用户无感知卡死是常态。
为什么不能用 enctype="multipart/form-data" 直接提交大文件
表单提交会强制浏览器将整个文件读入内存再拼装 multipart 数据,不是流式发送。一个 500MB 文件 = 至少 500MB ArrayBuffer 占用,Chrome 常直接崩溃;input.files 本身已是只读引用集合,调用 form.submit() 后你完全失去控制权——没法加进度、没法中断、没法重试某一片。
- 即便设了
accept和前端file.size校验,也拦不住用户禁用 JS 后拖入超限文件 -
FormData.append("files", input.files)看似方便,实则把全部File对象引用塞进内存,FileList超过 100 个就容易 OOM - 服务端收到的仍是单次请求,Nginx 的
client_max_body_size、框架的fileSize限制全得按最大单文件设,放大攻击面
必须用 file.slice() 分片 + fetch 手动上传
这是唯一可控路径:file.slice() 返回新 Blob,不加载全文到内存;配合 content-range 头和服务端分片合并逻辑,才能真正支撑 GB 级上传。
- 分片大小建议设为
5 * 1024 * 1024(5MB):太小导致 HTTP 头开销占比过高,太大易触发单次FileReader内存峰值 - 每次只发一片,
fetch的body直接传blob,不要包装进FormData(除非服务端明确要求 multipart) - 必须带请求头:
Content-Range: bytes 0-5242879/10485759,服务端靠它定位写入偏移 - 读取完成后立刻置
reader = null,避免闭包持有Blob引用阻碍 GC
并发控制别用 Promise.all,用串行或限流队列
浏览器对同域 fetch 并发有硬限制(通常 6 个),盲目并发会导致排队、超时、重试风暴,反而更慢更不可靠。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
立即学习“前端免费学习笔记(深入)”;
- 推荐每次最多并发 3 个请求,用
async/await循环控制,失败只重试当前片 - 每片请求必须携带唯一标识:
filename、chunkIndex、totalChunks、fileId(可用SubtleCrypto.digest预生成) - 服务端需按
fileId+chunkIndex原子写入临时目录,最后校验MD5或SHA256合并,前端不做“合并”逻辑 - 别依赖
XMLHttpRequest.upload.onprogress,现代fetch没原生进度事件,要用ReadableStream+tee()才能真正流式监听(但兼容性差,稳妥起见仍用分片级进度)
后端三道硬限制缺一不可
前端分片只是体验优化,绕过它只要 curl 传一个完整文件就行。Nginx、框架、语言层限制必须一致,否则会出现“前端卡住几秒后静默失败”这种最难排查的问题。
- Nginx:
client_max_body_size必须 ≥ 单片大小(如6m),不是总文件大小 - Node.js + multer:
limits: { fileSize: 6 * 1024 * 1024 },且fileFilter中必须校验req.headers["content-range"] - PHP:
upload_max_filesize和post_max_size可设小(如6M),但client_max_body_size在 Nginx 层已卡住,PHP 根本收不到请求 - 最容易漏的是:Nginx 设了
20m,multer 却只允许5m,用户上传 10MB 单片时,Nginx 放行,multer 拒绝,但错误响应可能被中间件吞掉,前端 fetch 只收到空响应或 timeout
真正的难点不在切片代码,而在服务端如何原子化接收、去重、校验、合并、清理临时分片;前端只需确保每一片干净、有序、可重入地送达——少一个 content-range,或多传一次重复片,后端就可能写坏文件。


















