如何实现文件上传时的百分比加载状态?
我在用 XMLHttpRequest 上传文件,想实时显示上传进度的百分比,但不确定怎么监听进度事件。
试过在 onprogress 里计算 (loaded / total) * 100,但有时候 total 是 0,导致 NaN,而且 UI 更新也不稳定。
下面是我现在的代码:
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(percent); // 有时正常,有时不触发
}
};
xhr.send(file);
是不是漏了什么设置?或者有没有更可靠的方式?
addEventListener替代onprogress,同时服务器必须返回Content-Length头才能拿到 total。如果服务器没返回这个头,lengthComputable就是 false,进度事件压根不会触发。如果服务器不是你写的,记得让他们加上
Content-Length响应头。关于 total 为 0 的问题
这种情况通常发生在服务器没有返回 Content-Length 响应头的时候。你已经用了 lengthComputable 判断,这很好,但如果服务器用了 chunked 编码传输,确实不会有 total 值。不过你说"有时不触发",这可能是另一个问题——进度事件本身触发次数不确定,有时候服务器响应慢或者文件较小时,进度事件会很少。
关于 UI 更新不稳定
这基本是因为进度事件触发太频繁了,浏览器来不及渲染。你需要做一个简单的节流处理。
我给你一个完整的解决方案:
几个关键点解释一下:
第一,lengthComputable 为 false 的情况,服务器必须在响应头里返回 Content-Length,文件上传接口一定要让后端加上这个头。如果是用 Node.js 的 koa 或者 express,很可能是没设置好。
第二,节流这个事很重要。进度事件每秒可能触发几十次,你每次都去操作 DOM 的话,浏览器会卡,UI 反而显示不稳定。100ms 的间隔对人类来说已经足够平滑了。
第三,onload 事件要单独处理,因为最后 100% 的时候 onprogress 不一定会触发(特别是小文件或者网络快的时候)。
第四,如果你的后端确实没办法返回 Content-Length(比如用了流式上传),那进度条只能显示"上传中...",无法显示具体百分比,这种情况要考虑用其他方案,比如分片上传然后合并。
你现在遇到的问题大概率是后端没有返回 Content-Length,你可以打开浏览器的 Network 面板,看看上传请求的响应头里有没有 Content-Length。