必须手动记录时间戳和字节数才能计算上传速度与剩余时间,因layui的progress回调仅提供四舍五入后的百分比n,不暴露e.loaded/e.total原始值,无法反推精确字节数;需在before中初始化状态、progress中持续更新并防除零/nan。
progress回调里怎么拿到上传速度和剩余时间
原生 xmlhttprequest.upload.onprogress 只提供已上传字节数(e.loaded)和总大小(e.total),layui 的 progress 回调不暴露原始事件对象,所以必须手动记录时间戳和字节数才能算出速度与预估剩余时间。
核心做法是在 before 阶段初始化计时器和初始字节数,在 progress 中持续更新并计算:
- 用
Date.now()记录首次进入progress的时间(不是before,因为此时文件还没开始发) - 每次回调取
e.loaded和e.total—— 这需要改写 Layui 的内部 XHR 实例,或通过 patch 方式注入监听(见下一条) - 速度 =
(loaded - lastLoaded) / (now - lastTime)(单位:KB/s),剩余时间 =(total - loaded) / speed(秒) - 注意防除零、NaN 和负值,尤其在刚启动或网络抖动时
为什么直接用Layui的progress(n)参数算不了速度
progress 回调的参数 n 是百分比整数(0–100),它被 Layui 内部四舍五入过,且不带时间戳、也不暴露 loaded/total 原始值。靠 n 反推字节数会丢失精度,尤其小文件或高频率回调时,n 可能连续多帧都是 0 或 100,根本无法拟合斜率。
真正可用的数据源只有原生 onprogress 事件的 event.loaded 和 event.total。这意味着你必须:
- 确保使用 Layui ≥ 2.5.5(否则没
progress回调) - 在
upload.render()的before中 return false,并手动创建XMLHttpRequest实例 - 把
xhr.upload.onprogress绑定到自定义函数,而不是依赖 Layui 封装后的progress - 不能同时用 Layui 的自动上传(
auto: true)和手动进度计算,二者互斥
多文件场景下如何为每个文件单独算速度和剩余时间
每个文件需独立维护自己的计时状态,不能共用同一组变量。常见错误是用全局 lastTime 和 lastLoaded,结果多个文件互相覆盖,速度忽高忽低。
正确做法是:在 before 回调中,根据 index 或文件名生成唯一 key,把状态存进 Map 或数组:
const uploadStates = new Map();
upload.render({
elem: '#upload',
url: '/upload',
auto: false,
multiple: true,
before: function(obj, index) {
uploadStates.set(index, {
startTime: 0,
lastTime: 0,
lastLoaded: 0,
total: obj.files[index].size
});
// … 启动手动上传
},
progress: function(n, elem, e, index) {
const state = uploadStates.get(index);
if (!state.startTime) state.startTime = Date.now();
const now = Date.now();
const speed = Math.round((e.loaded - state.lastLoaded) / ((now - state.lastTime) || 1) * 1000) / 1000; // KB/s
const remain = speed > 0 ? Math.ceil((state.total - e.loaded) / speed) : 0;
// 更新 UI:显示 “xx KB/s,约 xx 秒”
document.getElementById(`speed-${index}`).innerText = `${speed} KB/s`;
document.getElementById(`remain-${index}`).innerText = `约 ${remain} 秒`;
state.lastTime = now;
state.lastLoaded = e.loaded;
}
});
IE11 和旧版 Edge 不支持上传速度计算
XMLHttpRequest.upload.onprogress 在 IE10+ 才可用,但 IE11 和旧版 Edge 存在两个致命限制:
- 不触发
onprogress事件(尤其 HTTPS 站点下) - 即使触发,
e.loaded常常恒为 0,直到上传完成才跳到e.total,导致全程无法计算速度
这意味着在这些浏览器里,你只能显示百分比进度条,不能可靠显示速度和剩余时间。别试图 hack,直接降级处理:
- 检测
'onprogress' in xhr.upload,不支持就隐藏速度/剩余时间区域 - 服务端若支持分块上传(如 tus 协议),可改用轮询后端进度接口,但这是另一套架构,和 Layui 原生上传无关
- Nginx 或 PHP 的上传限制(如
client_max_body_size)一旦触发,浏览器会静默中断连接,onprogress停在某个值,此时速度归零、剩余时间无限大——这不是前端 bug,得查服务端日志
真正的难点不在公式,而在于把「瞬时速度」平滑成用户可读的数值:太快会跳变,太慢会滞后,中间还得过滤掉网络抖动造成的异常峰值。没人会盯着“12.345 KB/s”看,但“约 2 分钟”这种提示,必须建立在连续 3 次采样都稳定的基础上。











