主题
上传进度
上传进度的核心不是“上传能不能成功”,而是“前端能不能知道已经传了多少”。
什么时候需要上传进度
典型场景:
- 图片、视频、附件上传
- 大文件上传
- 多文件批量上传
只要用户等待时间明显,就通常需要进度反馈。
浏览器里最常见的做法
原生前端里,最常见的上传进度来源是 XMLHttpRequest.upload 上的 progress 事件。
js
xhr.upload.addEventListener('progress', (event) => {
console.log(event.loaded, event.total)
})progress 事件里主要看什么
loaded:已经上传的字节数total:总字节数lengthComputable:是否能计算总量
常见百分比计算方式:
js
const percent = (event.loaded / event.total) * 100一个基础例子
js
const fileInput = document.getElementById('fileInput')
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0]
if (!file) return
const xhr = new XMLHttpRequest()
xhr.open('POST', '/upload-endpoint')
xhr.upload.addEventListener('progress', (event) => {
if (!event.lengthComputable) return
const percent = (event.loaded / event.total) * 100
console.log(percent.toFixed(2) + '%')
})
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.log('上传完成')
return
}
console.error('上传失败')
}
const formData = new FormData()
formData.append('file', file)
xhr.send(formData)
})为什么很多人说 fetch 不好做上传进度
因为原生 fetch 在大多数常见用法里,不像 XHR 那样直接提供成熟稳定的上传进度监听入口。
所以如果页面明确需要上传进度,通常更容易直接选择:
XMLHttpRequest- 基于 XHR 的库
Axios 为什么能做
Axios 浏览器端底层通常就是 XHR,所以可以用:
js
axios.post('/upload-endpoint', formData, {
onUploadProgress(progressEvent) {
const percent = (progressEvent.loaded / progressEvent.total) * 100
console.log(percent.toFixed(2) + '%')
},
})进度条卡在 99% 是什么情况
这是高频现象。
上传进度只能反映“浏览器把数据发出去多少”,不代表服务端已经处理完成。
所以常见情况是:
- 文件已经上传完
- 但服务端还在做校验、转码、落盘、合并分片
这时前端看起来就像卡在 99%。
进度事件触发过于频繁怎么办
进度事件可能非常密集,如果每次都直接更新 UI,容易造成频繁渲染。
这时通常会做节流:
js
import { throttle } from 'lodash'
const handleProgress = throttle((event) => {
if (!event.lengthComputable) return
const percent = (event.loaded / event.total) * 100
console.log(percent.toFixed(2) + '%')
}, 200)
xhr.upload.addEventListener('progress', handleProgress)使用建议
- 明确需要上传进度时,优先考虑 XHR 或基于 XHR 的方案。
- 进度条只代表传输进度,不代表服务端处理完成。
- 进度更新频繁时,记得节流 UI 刷新。
- 大文件上传场景下,上传进度通常还要和分片进度一起设计。
