fetch不支持进度事件,下载需用response.body.getreader()手动计算,上传必须用xmlhttprequest;服务端需返回content-length且cors暴露该头,否则无法显示百分比。

fetch 本身不支持上传/下载进度事件
直接用 fetch() 发起请求,拿不到 progress 事件——它压根没实现 XMLHttpRequest 那套 upload.onprogress 和 download.onprogress。这不是你写错了,是标准如此。想监控进度,必须绕道:要么换回 XMLHttpRequest,要么用 ReadableStream 手动读取响应体。
下载进度:用 Response.body.getReader() + 计算已读字节数
适用于大文件下载(如 PDF、视频),前提是服务端支持 Content-Length 或返回流式响应(如 Transfer-Encoding: chunked)。关键点:
-
Response.body是一个ReadableStream,调用.getReader()得到读取器 - 每次
reader.read()返回{ done, value },value是Uint8Array,长度即本次读取的字节数 - 需提前从
response.headers.get('content-length')拿总大小;若为null(比如 chunked 响应),只能显示“已接收 X 字节”,无法算百分比
const response = await fetch('/large-file.zip');
const total = response.headers.get('content-length');
const reader = response.body.getReader();
let loaded = 0;
<p>while (true) {
const { done, value } = await reader.read();
if (done) break;
loaded += value.length;
const progress = total ? Math.round((loaded / total) * 100) : loaded;
console.log(<code>下载中:${progress}${total ? '%' : ' bytes'}</code>);
}</p>
上传进度:必须用 XMLHttpRequest,fetch 无解
目前(截至 2024 年)fetch() 不暴露上传过程的底层流,FormData 或 ArrayBuffer 全部塞进去才发,中间无法拦截或监听。唯一可靠方案是回归 XMLHttpRequest:
- 创建
new XMLHttpRequest()实例 - 绑定
xhr.upload.onprogress监听上传进度(注意是xhr.upload,不是xhr) - 手动设置
Content-Type(尤其传FormData时,让浏览器自动生成 boundary) - 别忘了
xhr.responseType = 'json'等后续处理需要的配置
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
console.log(`上传中:${percent}%`);
}
};
xhr.open('POST', '/upload');
xhr.send(formData); // formData 自动设 Content-Type
兼容性与边界情况必须手动兜底
进度监控不是“加几行就能跑”,容易在这些地方翻车:
- 服务端没返回
Content-Length→ 下载进度只能显示字节数,不能算百分比 - CORS 请求没配
Access-Control-Expose-Headers: Content-Length→ 前端读不到content-length头 - 上传大文件时用户刷新页面 →
XMLHttpRequest中断,无恢复机制,得靠后端分片或断点续传支持 - 使用
ReadableStream时忘记reader.releaseLock()(虽非强制,但建议显式释放)
进度条只是表象,真正难的是服务端配合和异常链路覆盖——比如网络闪断后是重试还是提示?这部分没法靠前端单方面解决。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











