html无原生上传进度能力,必须用xmlhttprequest.upload.onprogress监听字节级上传过程;需检查event.lengthcomputable防nan,且依赖后端返回content-length头,否则进度条失效。

HTML 本身不提供上传进度反馈能力,loading 效果必须靠 JavaScript 主动监听和驱动,核心依赖 XMLHttpRequest.upload.onprogress —— 这是唯一稳定可用的原生方案。
为什么不能只用 <input type="file"> + CSS?
很多人误以为加个 disabled 或切换按钮文字就算“loading”,但这只是静态遮蔽,完全不反映真实上传状态。用户可能卡在 0% 半分钟没反应,根本不知道是网络卡了、服务端崩了,还是前端压根没发出去。真正的 loading 必须绑定字节级上传过程。
-
<input type="file">只负责选文件,不参与传输,也没有进度属性 - 表单 submit 默认整页跳转,无法拦截并监听上传流
- 任何纯 CSS 动画(如旋转图标)都只是“假装在传”,和实际网络行为脱钩
XMLHttpRequest.upload.onprogress 怎么写才不白监听
监听函数被触发 ≠ 一定能算出百分比。关键要检查 event.lengthComputable,否则 event.total 是 0,所有计算都会崩成 NaN%。
- 必须写
if (event.lengthComputable)再算(event.loaded / event.total) * 100 -
event.loaded是已发出字节数,event.total来自服务端响应头Content-Length—— 如果后端没返回这个头,lengthComputable就是false - 不要监听
xhr.onprogress(那是下载阶段),必须监听xhr.upload.onprogress - 示例片段:
const xhr = new XMLHttpRequest();<br>xhr.open('POST', '/upload');<br>xhr.upload.onprogress = (e) => {<br> if (e.lengthComputable) {<br> const pct = Math.round((e.loaded / e.total) * 100);<br> document.getElementById('bar').style.width = <code>${pct}%</code>;<br> }<br>};
常见 loading 失效的三个后端原因
前端代码写对了,但进度条不动/卡在 0%/直接跳到 100%,90% 概率是后端没配好。
- 服务端未返回
Content-Length响应头(尤其 Nginx 反向代理时默认不透传) - 使用了 streaming 解析(如某些 FastAPI 或自定义中间件),导致连接保持但不声明总长
- 后端框架(如 Express + multer)字段名不匹配:
formData.append('file', file)要和后端multer.single('file')的 key 严格一致,否则解析失败,请求可能静默终止
别碰 fetch 的上传进度幻想
虽然网上有“用 ReadableStream 分块 + AbortController 手动计数”的方案,但实际踩坑极多:
- Chrome 对
fetch上传流的支持不稳定,Safari 直接不触发upload事件 - 需要自己 chunk 文件、拼 boundary、处理 multipart 头 —— 等价于重写 FormData
- 一旦服务端提前 close 连接,前端无法区分是上传失败还是进度丢失
- 真要简化开发,直接用
axios:它内部封装了XMLHttpRequest,且onUploadProgress配置项开箱即用
真正难的从来不是写几行 JS,而是前后端对齐传输语义:谁声明长度、谁按名收字段、谁保证连接不被中间件截断。进度条卡住时,先抓包看响应头,再查后端日志,最后才回来看前端逻辑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











