Skip to content

上传进度

上传进度的核心不是“上传能不能成功”,而是“前端能不能知道已经传了多少”。

什么时候需要上传进度

典型场景:

  • 图片、视频、附件上传
  • 大文件上传
  • 多文件批量上传

只要用户等待时间明显,就通常需要进度反馈。

浏览器里最常见的做法

原生前端里,最常见的上传进度来源是 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 刷新。
  • 大文件上传场景下,上传进度通常还要和分片进度一起设计。

基于 MIT 许可发布