xmlhttprequest 无法直接获取多文件总进度,需手动聚合各文件上传量并除以总大小;须用 requestanimationframe 或时间阈值节流 ui 更新,避免卡顿;并发上传时需维护每个文件状态,失败后清零已传量以防进度卡在99%。

用 XMLHttpRequest 逐个上传并聚合进度
浏览器原生不提供「多文件总进度」的 API,XMLHttpRequest.upload.onprogress 只反映单个请求的进度。想显示整体完成度,必须自己统计:把所有文件的上传量加起来,除以总大小。关键不是“怎么监听”,而是“怎么对齐多个异步上传的进度流”。
实操建议:
- 上传前先遍历
input.files,累加file.size得到totalSize - 为每个文件创建独立的
XMLHttpRequest,并在其upload.onprogress回调中更新该文件的已上传字节数(存进数组或 Map) - 每次回调都重新计算当前总上传量:
uploadedSoFar = filesProgress.reduce((a, b) => a + b, 0),再算百分比 - 注意:不能直接用
event.loaded累加——它只代表当前请求的已传量,不是全局累计值
避免 onprogress 频率过高导致 UI 卡顿
onprogress 在大文件上传时可能每毫秒触发多次,频繁更新 DOM(比如反复设置 progress.value)会拖慢主线程,尤其在低端设备上明显卡顿。
实操建议:
- 用
requestAnimationFrame节流 UI 更新:只在下一帧渲染前汇总一次进度,而不是每次onprogress都更新 - 或者简单点,加个时间阈值:记录上一次更新时间,间隔小于 100ms 的跳过
- 别在
onprogress里做耗时操作,比如遍历 DOM 节点、格式化字符串、调用复杂函数
File API 与 FormData 的边界问题
很多人以为把多个 File 对象塞进一个 FormData 就能“合并上传”,但其实后端收到的仍是多个独立字段(除非你手动拼二进制流)。这意味着:即使只发一个请求,XMLHttpRequest.upload.onprogress 依然只能告诉你这个「单请求」的进度,无法拆解出每个文件各自传了多少。
所以真实场景中,「总进度」几乎总是基于「多请求并发」实现的。要注意:
- 并发数别设太高(比如 >6),否则 Chrome 会自动排队,反而让进度条“卡住不动”
- 后端必须支持相同接口被多次调用(比如 /upload 接收单个文件),且不依赖 session 或临时 token 绑定顺序
- 如果后端要求必须单请求上传多文件,那总进度就不可靠——你只能显示“整个请求的上传进度”,无法知道第 3 个文件传到哪了
上传失败时的进度状态怎么处理
某个文件上传失败(比如网络中断、413 Payload Too Large),onerror 或 onloadend 触发后,它的进度应冻结在最后值,还是归零?这取决于 UX 设计,但技术上容易忽略的是:失败后如果不主动从总进度中扣除该文件的已传量,会导致最终完成度永远达不到 100%。
实操建议:
- 为每个文件维护状态:{ size, uploaded, status: 'pending' | 'uploading' | 'success' | 'error' }
- 在
onerror中把该文件的uploaded设为 0,并标记status = 'error';这样总进度计算时,它贡献 0 字节 - 如果允许重试,重试前要清空该文件的
uploaded,否则可能重复累加
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











