上传一个 200MB 的安装包,进度条走到满格,界面停在那不动,过了十几秒才提示成功。用户以为卡死了,其实是进度算错了。

用 XMLHttpRequest 时,上传进度在 xhr.upload.onprogress 里,event.loaded 除以 event.total 就是百分比。注意事件挂在 upload 上,挂在 xhr 本身拿到的只有下载进度,上传文件时几乎不动。

抽成一个小函数

xhr.upload.onprogress = function (e) { if (!e.lengthComputable) return; var p = Math.round(e.loaded / e.total * 100); bar.style.width = p + '%'; };

lengthComputable 为 false 时 total 是 0,除法会得到 Infinity 或者 NaN,进度条直接变成一条全满的色块。这就是最常见的假 100%。

传完了为什么还要等

浏览器把数据推给服务器,只是传输结束。服务端接收、校验、落盘、返回响应,这段时间没有字节可统计。想覆盖后半段,只能让服务端回传处理进度,或者把文案切成上传中和处理中两段。

fetch 目前拿不到上传进度,请求体的流式上传在浏览器里还没铺开。要真进度就得用 XHR,短期内不会变。

几处容易踩的点

  • 开启压缩传输后 total 和实际字节数对不上,进度会停在百分百之前。
  • 页面切到后台,回调被节流,跳动变不均匀,但数值本身不会错。
  • 多文件并发上传,每个文件单独算进度,再按字节数加权汇总成总进度。

纯展示用的进度条,用 CSS 动画跑个假进度更省事,反正用户只关心它在动。要传大文件、让用户判断还要多久的,老老实实读 loaded 和 total。