Fetch API 默认不支持上传进度监听,因其基于Promise设计且请求体一次性写入;推荐用XMLHttpRequest实现带进度的文件上传,或采用Fetch分块上传手动计算进度。

JavaScript 中使用 Fetch API 上传带进度条的文件,核心在于:Fetch 本身不提供上传进度事件,但可以通过 ReadableStream + pipeTo 或更实际的方式——用 XMLHttpRequest(原生支持 upload.onprogress);不过若坚持用 Fetch,可借助 AbortController + 流式分块上传 + 手动计算进度,或更推荐的方案是:用 fetch 配合 FormData,再通过监听 XMLHttpRequest 的上传事件来补足进度能力(因 fetch 不暴露上传流)。但注意:标准 Fetch 规范至今(2024)仍未支持上传进度监听。
为什么 Fetch 默认没有上传进度?
Fetch 是基于 Promise 的设计,关注请求发起与响应接收,其底层 Request body 是一次性写入的(如 Blob、FormData),浏览器不暴露上传过程中的字节流状态。而 XMLHttpRequest 的 upload 属性专门为此设计,能触发 onprogress 事件。
实用方案:用 XMLHttpRequest 实现带进度的文件上传(推荐)
虽然问题问的是 Fetch,但生产环境追求稳定和兼容性时,XMLHttpRequest 仍是上传进度的可靠选择。代码简洁清晰:
function uploadWithProgress(file, url) {
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append('file', file);
<p>xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(<code>上传中:${percent.toFixed(1)}%</code>);
// 更新页面上的进度条元素,例如:
// progressBar.style.width = <code>${percent}%</code>;
}
};</p><p>xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.log('上传成功', xhr.responseText);
} else {
console.error('上传失败', xhr.statusText);
}
};</p><p>xhr.onerror = () => console.error('网络错误');
xhr.open('POST', url);
xhr.send(formData);
}</p><p>// 调用示例
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
uploadWithProgress(e.target.files[0], '/api/upload');
}
});
进阶:Fetch + 分块上传(手动模拟进度)
若必须用 Fetch,且文件较大、需可控进度,可将文件切片(Blob.slice),逐块上传并累加已上传字节数:
立即学习“Java免费学习笔记(深入)”;
- 用
file.slice(start, end)按固定大小(如 64KB)分块 - 每块用 fetch POST 上传,携带序号、总块数、文件名等元数据
- 服务端需支持追加写或最终合并;前端按顺序更新进度百分比:
(uploadedChunks / totalChunks) * 100 - 优点:可控、可断点续传;缺点:逻辑复杂,需前后端协同,不适合小文件
注意事项与兼容性
- FormData 上传大文件时,注意服务端配置(如 Nginx 的
client_max_body_size、Node.js 的 body-parser 限制) - Chrome/Firefox/Safari 均支持
xhr.upload.onprogress,IE10+ 也支持 - Fetch 上传文件本身没问题:
fetch(url, { method: 'POST', body: formData }),只是缺进度反馈 - 不要尝试给 fetch 的 Request.body 绑定事件——它不是可读流(除非你手动构造 ReadableStream 并监听,但上传阶段仍不可见)
不复杂但容易忽略:真正需要上传进度时,优先选 XMLHttpRequest;Fetch 更适合无状态、轻量、只关心结果的场景。两者并非互斥,可以共存于一个项目中,按需选用。


















