必须用XMLHttpRequest.upload.onprogress监听上传进度,且需在open()后、send()前绑定;event.lengthComputable为false时无法计算百分比,因服务端未返回Content-Length或被代理截断。

用 XMLHttpRequest.upload.onprogress 监听上传进度
HTML5 原生上传进度监控不依赖第三方库,核心就是监听 XMLHttpRequest 实例的 upload.onprogress 事件。注意不是 onload 或 onreadystatechange —— 那些只管完成,不管过程。
关键点:必须显式创建 XMLHttpRequest,不能靠表单默认提交;且需在调用 open() 之后、send() 之前绑定 upload.onprogress。
-
event.loaded表示已上传字节数,event.total是总字节数(仅当服务端返回Content-Length且浏览器支持 CORS 时才可靠) - 如果
event.lengthComputable === false,说明无法获知总大小(常见于 chunked 编码或服务端未设响应头),此时只能显示“上传中…”或用动画代替数值进度 - 不要在
onprogress里频繁更新 DOM(如每毫秒改一次innerHTML),建议加节流,比如用requestAnimationFrame或简单计数器限制更新频率
fetch 不支持上传进度?得换回 XMLHttpRequest
fetch API 设计上不暴露上传过程,目前(2026 年)所有主流浏览器仍不支持 UploadStream 或类似钩子。想用 fetch + 进度条,唯一可行路径是手动分块上传(把文件切片、逐片 fetch、自己累加已传量),但复杂度陡增,且服务端必须配合接收分片。
所以真实项目中,只要需要原生进度反馈,就老实用 XMLHttpRequest。别被“fetch 更现代”带偏——功能场景决定工具选择。
立即学习“前端免费学习笔记(深入)”;
- 用
FormData构造请求体时,直接 appendFile对象即可,XMLHttpRequest会自动设置正确的Content-Type: multipart/form-data; boundary=... - 如果后端要求 JSON 格式上传(如 base64),那
total就等于该字符串长度,可精确计算;但注意 base64 会使体积膨胀约 33%,且内存占用高,大文件慎用
服务端没返回 Content-Length?前端进度可能一直为 0%
浏览器能否拿到 event.total,不仅取决于前端代码,更取决于服务端响应是否包含 Access-Control-Allow-Headers: Content-Length 和实际返回了 Content-Length 响应头。但上传进度依赖的是 *请求体* 的长度,而 CORS 预检和响应头机制对此并无强制要求——真正起作用的是:服务端是否在上传开始前就明确告知了客户端“这次要传多少字节”。
实际上,绝大多数 HTTP 服务器(如 Nginx、Apache)默认不主动提供上传总长;Node.js 的 express + multer 也不返回它。因此 event.lengthComputable 经常为 false。
- 解决思路一:前端先读取
File.size赋给total(仅限用户本地选中的文件,对Blob或流式生成内容不适用) - 解决思路二:后端在接收到
Content-Length请求头后,在响应预检请求(OPTIONS)时带上Access-Control-Expose-Headers: Upload-Length,再自定义返回该字段——但这需要服务端深度配合,非标准做法 - 最务实的做法:接受“无法显示百分比”的现实,用确定性动画(如旋转图标 + “正在发送…”)比强行显示“0%”更可信
移动端 Safari 对 XMLHttpRequest.upload 的兼容细节
iOS 15.4+ 已完整支持 upload.onprogress,但有两个易踩坑点:
- 如果页面启用了
viewport的user-scalable=no或禁用双击缩放,某些 iOS 版本下onprogress触发频率极低(甚至只触发一次),建议移除缩放限制或改用touch-action: manipulation - Safari 对
File对象的lastModified和webkitRelativePath支持不稳定,但不影响size和name,进度计算只依赖前者,基本无碍 - 真机调试时,用
console.log(event.loaded, event.total, event.lengthComputable)看三连值,比看 UI 更快定位问题
进度条真正的难点不在怎么画,而在怎么让 total 可信、让 loaded 不跳变、让移动端不掉帧——这些都藏在浏览器和网络栈的缝隙里,得一层层验。



















