必须用xmlhttprequest.upload.onprogress驱动progress标签,因fetch和表单submit均不支持上传进度;e.lengthcomputable为false时无法计算百分比,主因是服务端未返回content-length或cors未暴露该头。

progress 标签本身不自动获取进度,必须靠 JavaScript 驱动 —— 用 XMLHttpRequest.upload.onprogress 拿到实时字节数,再算百分比填进去。Fetch 不行,表单 submit 也不行,这是唯一可靠路径。
为什么 upload.onprogress 有时返回 0% 或 unable to compute
核心原因是 event.lengthComputable === false,常见于以下几种情况:
- 服务端没返回
Content-Length响应头(比如 Node.js 的 Express 默认不设,需手动加res.set('Content-Length', fileSize)) - 用了 CORS 且未配置
Access-Control-Expose-Headers: Content-Length - 上传时启用了
withCredentials: true,但服务端未在预检响应中暴露长度头 - 后端用了流式处理(如直接 pipe 到 S3),根本没提前知道总大小
此时只能显示“正在上传…”或用骨架动画,硬算 e.loaded / e.total * 100 会得 NaN 或 0。
FormData + XMLHttpRequest 的最小可行写法
别绕开 XMLHttpRequest 去套 Fetch 或 jQuery.ajax —— 后者封装层可能吞掉 upload 对象。下面是最简可运行结构:
const input = document.getElementById('fileInput');
const progress = document.getElementById('uploadProgress');
<p>input.addEventListener('change', () => {
const file = input.files[0];
if (!file) return;</p><p>const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
progress.value = percent;
progress.textContent = <code>${percent}%</code>;
}
};</p><p>xhr.onload = () => console.log('done');
xhr.onerror = () => console.error('fail');</p><p>const fd = new FormData();
fd.append('file', file);</p><p>xhr.open('POST', '/upload');
xhr.send(fd);
});
</p>
注意点:
-
progress元素的max属性默认是 100,所以直接赋percent就行;若设成字节数,就得同步改max -
fd.append()的第二个参数必须是File对象,不是files[0].name或字符串 - 不要在
onprogress里频繁操作 DOM,建议加节流:用requestAnimationFrame或简单地if (Date.now() - lastUpdate > 200)
多文件上传时 e.total 是什么
它等于所有被 append 进 FormData 的文件总字节数,不是单个文件大小。例如:
- 你 append 了 3 个文件:1MB、2MB、5MB →
e.total === 8388608(8MB) - 上传中途某个文件失败,
e.loaded仍累计之前成功上传的字节,不会重置 - 想单独看每个文件进度?得拆成多次独立
xhr请求,不能塞进一个FormData
另外,input[type="file"][multiple] 选中多个文件后,files 是 FileList,不是数组,遍历时要用 Array.from(files) 或循环索引。
真实项目里最容易被忽略的,是服务端是否真正支持并暴露了长度信息 —— 前端代码写得再对,后端没配好 Content-Length 或 CORS 头,e.lengthComputable 就永远是 false。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











