fetch 不支持上传进度监听,必须用 xmlhttprequest 手动构建 formdata 并监听 xhr.upload.onprogress;不可混用 fetch 与 xhr;现代 readablestream 方案兼容性差,生产环境推荐 xmlhttprequest。

Fetch 本身不支持上传进度监听
这是最常被误解的一点:fetch() API 没有内置的上传进度事件(如 upload.onprogress),它只提供响应体读取阶段的流式控制(response.body.getReader()),对请求体上传过程完全不可见。所以想用纯 fetch() 监听文件上传进度,做不到。
必须用 XMLHttpRequest 手动构造 multipart 表单上传
要监听上传进度,得回到 XMLHttpRequest,并手动构建 FormData 和请求头。关键是不能让浏览器自动设置 Content-Type(否则会带 boundary,但你无法在 fetch 中拿到该 boundary 来模拟)——而 XMLHttpRequest 在传入 FormData 时会自动处理 boundary 和编码,且暴露 xhr.upload.onprogress。
实操要点:
-
FormData实例直接 append 文件:formData.append('file', fileInput.files[0]) - 创建
XMLHttpRequest,监听xhr.upload.onprogress获取event.loaded和event.total - 调用
xhr.send(formData)—— 不要手动设Content-Type,让浏览器自动生成 multipart header - 若后端要求特定字段名或额外参数,统一 append 到
FormData,不要拼 query string
Fetch + XMLHttpRequest 混用?没必要,也难协调
有人试图“用 fetch 发起请求,再用 xhr 去劫持进度”,这不可行。两个 API 底层是独立通道,不存在共享上传实例的机制。所谓“混用”只会增加复杂度,还可能因 CORS、凭证(credentials)、header 冲突等问题失败。
常见翻车点:
- 给
XMLHttpRequest错误地设置了Content-Type: multipart/form-data—— 这会导致 boundary 缺失,后端解析失败 - 在
onprogress里频繁触发 UI 更新(如每毫秒 setState),引发卡顿;建议用throttle或仅在loaded变化超过 1% 时更新 - 忽略
file.size === 0或file.name为空的边界情况,导致上传后端报错或空文件
现代替代方案:ReadableStream + upload streaming(实验性)
Chrome 115+ 支持通过 fetch() 传入 ReadableStream 并配合 TransformStream 手动分块上传,从而实现进度控制。但这需要你自己切片、计算 offset、拼接 boundary,并处理流背压,兼容性差(Safari / Firefox 尚未支持),生产环境慎用。
简单说:除非你明确需要流式分块上传(比如上传超大文件且要断点续传),否则老实用 XMLHttpRequest + FormData 最稳。进度监听这件事,不是“新不如旧”,而是“API 职责不同”——fetch 定位是通用资源获取,上传进度属于传输层细节,本就归 XMLHttpRequest 管。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











