前端如何实现文件上传进度条?实时进度实现方法详解

文件上传进度条的实现原理

在前端实现上传进度条,本质是监听文件上传过程中的数据传输进度。文件上传本质是二进制数据从浏览器发送到服务器的过程,浏览器会持续返回已上传字节数(loaded)和总字节数(total)。通过计算:上传进度 = loaded / total * 100,即可得到实时进度百分比。

核心实现方式:XMLHttpRequest(推荐)

目前最主流、最稳定的方式是使用 XMLHttpRequest 的 upload.onprogress 事件。

1. 基础实现代码

<input type="file" id="file">
<div id="progressBar" style="width:300px;height:20px;background:#eee;">
  <div id="progress" style="height:100%;width:0;background:green;"></div>
</div>

<script>
document.getElementById('file').addEventListener('change', function() {
  const file = this.files[0];
  const formData = new FormData();
  formData.append('file', file);

  const xhr = new XMLHttpRequest();

  // 监听上传进度
  xhr.upload.onprogress = function (e) {
    if (e.lengthComputable) {
      const percent = Math.round((e.loaded / e.total) * 100);
      document.getElementById('progress').style.width = percent + '%';
    }
  };

  xhr.open('POST', '/upload');
  xhr.send(formData);
});
</script>

2. 关键点解析

  • xhr.upload.onprogress:监听上传进度
  • e.loaded:已上传字节
  • e.total:总字节数
  • e.lengthComputable:是否可计算进度

该方式是浏览器原生支持,性能稳定,兼容性最好。

为什么不推荐 Fetch 实现?

很多开发者会问:能不能用 fetch?答案是:可以上传,但很难做进度条。

因为fetch 默认不支持上传进度回调,需要 ReadableStream 手动实现(复杂度极高)。

进阶:React/Vue 中的封装思路

在实际项目中,通常会封装成 Hook 或组件。

React Hook 示例:

import { useState } from 'react';

export function useUpload() {
  const [progress, setProgress] = useState(0);

  const upload = (file) => {
    const xhr = new XMLHttpRequest();
    const formData = new FormData();
    formData.append('file', file);

    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable) {
        setProgress(Math.round((e.loaded / e.total) * 100));
      }
    };

    xhr.open('POST', '/upload');
    xhr.send(formData);
  };

  return { progress, upload };
}

页面中直接绑定进度值即可实现动态进度条。

大文件上传优化(实战必备)

1. 分片上传(Chunk Upload)

适用于大文件(如视频、压缩包):

const chunkSize = 5 * 1024 * 1024; // 5MB
const totalChunks = Math.ceil(file.size / chunkSize);

优势:

 
  • 避免上传失败重传
  • 支持断点续传
  • 提高成功率

2. 进度条优化建议

常见UI方案:

  • 线性进度条(最常见)
  • 圆形进度条
  • 分段进度条(分片上传)

3. 异常处理

xhr.onerror = () => alert('上传失败');
xhr.onload = () => console.log('上传成功');

常见问题总结

 

1. 为什么进度条不动?

可能原因:

  • 后端未返回 Content-Length
  • 使用 chunked 传输
  • lengthComputable = false

2. 多个文件如何处理?

Array.from(files).forEach(file => {
  formData.append('files[]', file);
});

3. 如何显示上传速度?

speed = loaded / (当前时间 - 开始时间)

总结

 
 

实现前端文件上传进度条的核心要点:

  • 首选 XMLHttpRequest.upload.onprogress
  • 用 loaded / total 计算百分比
  • UI实时更新进度条
  • 大文件建议使用分片上传
  • fetch 不适合做上传进度

上传进度条 = XHR监听 + 百分比计算 + UI更新。

评论