我用 XMLHttpRequest 上传文件,但 progress 事件好像没触发,进度条一直不动,咋回事?
我已经绑定了 onprogress 回调,也设置了 xhr.upload.onprogress,但控制台完全没输出。是不是哪里漏了?
顺便贴一下我写的进度条样式:
.upload-progress {
width: 0%;
height: 8px;
background: #4caf50;
transition: width 0.3s ease;
border-radius: 4px;
}
XMLHttpRequest的upload对象来监听onprogress?这个细节特别关键,因为上传进度是绑定在xhr.upload上的,而不是xhr本身。但光绑定还不够,很多人以为只要写了
xhr.upload.onprogress = function(e)就能触发,结果发现完全没反应。其实还有几个常见坑:第一,服务端必须正确返回
Content-Length响应头,或者至少不能是Transfer-Encoding: chunked(除非你服务端支持 chunked 的进度计算,但大多数场景不推荐)。浏览器是根据响应头里的Content-Length来估算上传进度的,如果你的后端没传这个头,或者用了分块传输,浏览器就无法知道“总共要传多少”,自然不会触发进度事件。第二,你得确认上传的文件是不是太小了——比如只有几 KB,上传时间不到 100 毫秒,那浏览器可能直接“跳过”了进度事件,或者触发得太快,肉眼根本看不到。你可以故意传个大文件试试(比如 50MB 的二进制文件),或者在本地用
setTimeout模拟延迟。第三,也是最容易被忽略的一点:你必须先设置好
onprogress,再调用send()。顺序不能反,不然事件监听器还没挂上就发请求了,当然收不到回调。下面给你一个完整能跑的示例,直接复制就能用(后端假设是能正常接收文件的接口,比如一个简单的
POST /upload):var fileInput = document.querySelector('#fileInput');var progressBar = document.querySelector('.upload-progress');var xhr = new XMLHttpRequest();xhr.open('POST', '/upload');// 关键:先绑定 onprogressxhr.upload.onprogress = function(e) {if (e.lengthComputable) { // 确保长度可计算var percent = (e.loaded / e.total) * 100;progressBar.style.width = percent + '%';console.log('上传进度:', percent.toFixed(2) + '%');}};xhr.onload = function() {if (xhr.status === 200) {console.log('上传成功');}};xhr.onerror = function() {console.error('上传出错');};// 最后才 sendvar formData = new FormData();formData.append('file', fileInput.files[0]);xhr.send(formData);另外你贴的进度条样式没问题,但建议加个外层容器,不然宽度从 0% 开始会有点突兀:
<div class="progress-container" style="width: 300px; height: 8px; background: #ddd; border-radius: 4px;"><div class="upload-progress" style="width: 0%; height: 100%; background: #4caf50; transition: width 0.2s ease;"></div></div>这样进度条就是从左往右填充,视觉更自然。
如果你试了上面这些还是没触发,大概率是服务端的问题。你可以用浏览器开发者工具的 Network 面板,看上传请求的 “Size” 列是不是显示了
0 B或者(pending),如果是,说明根本没发出去;如果显示了文件大小但没Content-Length,那后端确实没传这个头。最后说个冷知识:有些反代服务器(比如 Nginx)会默认吞掉
Content-Length,改用 chunked 传输。如果你用的是 Nginx 做反向代理,可以加一句proxy_set_header Transfer-Encoding chunked;试试,不过更稳妥的是直接让后端返回正确的Content-Length。总之记住三件事:
1. 绑定在
xhr.upload.onprogress上2. 保证上传数据长度可计算(有 Content-Length)
3. 事件监听要在 send() 之前挂好
你先按这个改,如果还不行,把你的后端接口类型(Node?PHP?Java?)说一下,我再帮你看看是不是服务端的问题。