原生提交不支持流式上传,因浏览器必须将整个文件读入内存构造完整multipart请求体,内存峰值与文件大小相当;真正流式需用fetch配合file.stream()及content-range分片。

原生 <form></form> 提交不支持流式传输,无论怎么配 enctype 或 method,浏览器都会把整个文件读进内存再发出去——这是浏览器底层实现决定的,不是配置能绕过的。
为什么 <form enctype="multipart/form-data"></form> 不等于流式上传
表单提交是同步构造完整 multipart 请求体的过程:浏览器必须先拼出带 boundary 的二进制块,这要求所有文件内容已加载到内存。选中一个 3GB 视频,Chrome 内存峰值就飙到 2.9+ GB,UI 卡死、甚至崩溃是常态。
-
enctype="multipart/form-data"只定义协议格式,不控制传输节奏 -
input[type="file"].files是引用,但submit()会强制触发全量 Blob 读取 - 服务端看到的
Content-Length是总大小,但浏览器早在发包前就已完成内存加载
真正可行的流式路径:用 fetch + file.stream()
只有显式调用 fetch 并传入 file.stream() 返回的 ReadableStream,浏览器才会边读磁盘边发包,内存占用稳定在几 MB 级别。
- 不能写
fetch(url, { method: 'POST', body: file })—— 这仍会全量加载 - 必须用
file.stream(),不能用FileReader.readAsArrayBuffer(file)(后者也是全量) - 需手动设置
Content-Range头,例如bytes 0-1048575/4294967296,服务端靠它定位写入位置 - Safari 和 Firefox 支持
ReadableStream上传;Chrome 更稳定;旧浏览器需降级为分片 +XMLHttpRequest
服务端接收流式上传的关键约束
后端不能依赖传统 $_FILES(PHP)或 req.file(Express),必须直接读取原始请求流,并按 Content-Range 分段落盘。
- PHP 默认行为是在脚本执行前把文件写进
/tmp,无法拦截原始 multipart 流——这不是 bug,是设计使然 - Node.js 需用
req.on('data', chunk => {...})或pipeline(req, writable)接收流,禁用body-parser中间件 - 若用 AWS S3,推荐客户端直传:PHP 只生成预签名 URL 或 POST 策略,避免服务器中转
最容易被忽略的一点:流式上传 ≠ 自动分片。你得自己切块、维护 offset、重试失败块、合并校验——file.stream() 只解决“边读边发”,不解决“断点续传”和“并发控制”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











