progress标签不感知上传,真正驱动更新的是XMLHttpRequest.upload.onprogress事件;常见失效原因包括监听对象错误、lengthComputable为false、后端未返回Content-Length。

progress 标签本身不感知上传,它只是个“画布”;真正驱动进度更新的是 XMLHttpRequest.upload.onprogress 事件。用错监听对象、没检查 lengthComputable、后端没返回 Content-Length,这三类问题占了进度条失效的 90% 以上。
为什么 upload.onprogress 不触发或始终为 0%
不是代码写错了,大概率是环境链路断在某处:
- 监听对象写成
xhr.onprogress—— 这是下载事件,上传阶段完全不触发;必须用xhr.upload.onprogress -
event.lengthComputable === false:说明浏览器拿不到event.total,常见于服务端未返回Content-Length响应头,或被 Nginx/CDN 截断(尤其开了proxy_buffering on) - 请求还没发出去就绑事件:必须在
xhr.open()之后、xhr.send()之前绑定,否则事件注册失败 - 用了
fetch却指望它触发上传进度 —— 所有主流浏览器至今(2026 年)都不支持原生上传进度钩子
如何正确绑定并更新 progress 元素
别直接在 onprogress 回调里狂改 value 或 innerHTML,容易卡顿且数值跳变:
- 先判断
if (e.lengthComputable),否则e.total是 0,除法会得NaN或Infinity - 百分比建议保留一位小数:
Math.round((e.loaded / e.total) * 1000) / 10,避免显示99.9999999% - DOM 更新加节流:用
requestAnimationFrame替代同步赋值,比如requestAnimationFrame(() => progressEl.value = percent) -
progress的max默认是 100,适合百分比;若想用字节单位,需同步设progressEl.max = file.size并更新value为e.loaded
多文件上传时每个进度条怎么独立显示
一个 XMLHttpRequest 实例只能对应一次上传,不能复用它处理多个文件:
立即学习“前端免费学习笔记(深入)”;
- 每个文件必须新建一个
xhr实例,并单独绑定自己的onprogress回调 - 进度 DOM 元素要按文件索引区分,比如
document.getElementById('progress-' + i),避免所有进度都刷到同一个progress上 - 不要把整个
FileList直接塞进FormData.append('files', files)——files是类数组,后端收不到;应遍历后逐个append('file', files[i]) - 若需汇总总进度(如“5 个文件,已完成 3 个”),得自己维护计数器,
onload里累加完成数,不能依赖onprogress的累计值
最常被忽略的一点:前端看到的 e.total 是否可靠,不取决于你写了什么 JS,而取决于服务端是否在请求开始前就明确告诉浏览器“这次要传多少字节”。这个信息藏在底层 HTTP 协议交互中,调试时别只盯着控制台,先用 curl -v 看响应头有没有 Content-Length,再查代理配置是否静默丢弃了它。



















