JavaScript 获取上传进度的几种方式实现

作者:异步漫步

时间:2026-10-30

浏览:530

本文详解前端获取文件上传进度的多种方案。涵盖使用 Axios 的 onUploadProgress、原生 Ajax 的 XMLHttpRequest 事件以及 Fetch API 的 onprogress 回调。针对多文件上传,介绍统一进度条与独立进度条的实现逻辑。此外,深入解析大文件分片上传的优势、切片索引处理及断点续传机制,帮助开发者优化上传体验。

文章目录

获取上传进度的几种方式

近期测试反馈指出,当前的文件上传等待界面缺乏动态加载效果,视觉体验不佳,需要优化。产品部门建议参考其他业务线的上传交互设计。起初认为仅需添加一个静态加载图标,但发现其他业务线采用了环形或线性的动态进度条。由于现有接口并未返回实时进度数据,且当前采用逐个文件分批次调用的方式,若仅依据上传完成的文件数量来估算进度,因文件大小差异较大,会导致进度显示极不准确。

随后与另一位前端同事交流,得知 Axios 提供了监听整个上传进度的 API。基于此信息,我对文件上传的进度监听机制进行了深入研究,并整理出以下总结。

前端使用axios实现监听上传进度

首先介绍如何使用 Axios 实现上传进度的监听,具体实现代码如下:

axios.post('/api/file/uploadfile', form, {
  headers: {
    'Content-Type': 'multipart/form-data',
  },
  onUploadProgress: (progressEvent) => {
    if (progressEvent.lengthComputable) {
      this.progresss = Math.round((progressEvent.loaded / progressEvent.total) * 100);
    }
  },
})
  .then((res) => {
   // ...
  });

前端使用ajax获取上传进度

接下来探讨如何利用原生 Ajax 获取上传进度,实现方式如下:

const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
  if (event.lengthComputable) {
    const progressPercentage = Math.round((event.loaded / event.total) * 100);
    console.log(`上传进度: ${progressPercentage}%`);
  }
});

前端使用fetch API获取上传进度

介绍如何使用 Fetch API 获取上传进度,具体代码如下:

const file = document.getElementById('fileInput').files[0];
fetch('/upload', {
  method: 'POST',
  body: file,
  onprogress: (event) => {
    if (event.lengthComputable) {
      const progressPercentage = Math.round((event.loaded / event.total) * 100);
      console.log(`上传进度: ${progressPercentage}%`);
    }
  },
})
  .then((response) => {
    // 处理响应结果
  })
  .catch((error) => {
    // 处理错误
  });

上述几种方法主要用于展示单一的上传进度条。那么,还有哪些其他方式来展示进度条呢?

多文件上传展示一个进度条

如果系统支持多文件上传,并通过文件数量来展示进度条,通常该如何实现?

假设需要上传 10 个文件,在上传第一个文件时,进度条应控制在 10% 以下,后续文件依此类推。具体实现代码如下:

let progress = 0;
const UPLOAD_TYPE_MAP = {
  UPLOADING: 'uploading',
  COMPLATE: 'complate',
}
const files = [{ file:'', status: UPLOAD_TYPE_MAP.UPLOADING }, { file:'', status: UPLOAD_TYPE_MAP.UPLOADING }];
for (let index = 0; index < files.length; index++) {
  const element = files[index];
  // 1、这里是调接口的逻辑,可以是并行也可以完成一个调下一个,这个看你们的接口的限速吧,我就按照并行做了。
  // 2、这里是每个文件上传完成的逻辑,更改当前文件的状态把status 改成UPLOAD_TYPE_MAP.COMPLATE
}
const timer = setInterval(() => {
  const eachProgress = 100 / files.length; // 计算单个的比例
  const complateUpload = files.filter((item) => item.status === UPLOAD_TYPE_MAP.COMPLATE);
  const upperLimit = (complateUpload.length + 1) * eachProgress;
  const maxProgress = upperLimit > 100 ? 100 : upperLimit; // 计算出当前最大进度
  if(progress < maxProgress) {
    progress++; // 边界控制
  }
  if(maxProgress === 100) {
    progress = 100;
    clearInterval(time);
  }
}, 500);

以上展示了多文件共用一个进度条的具体代码。需要注意的是,这种方法并不完全准确,因为文件大小存在差异。如果文件大小相近,该方案尚可接受。实际上,最佳方案是利用 Axios、Ajax 等技术分别为每个文件展示独立的进度条,具体选择需视业务需求而定。

大文件分片上传展示进度条

首先简要说明分片上传的优势,具体如下:

  • 可靠性和鲁棒性:将大文件分割为小块上传,即使发生网络中断或意外情况,也只需重新上传失败的分片,无需重新上传整个文件,从而提高了上传的可靠性和鲁棒性。
  • 传输效率:大文件被分割为多个小块并行上传,可以充分利用网络带宽,从而提高传输速度和效率。
  • 断点续传:如果上传过程中断,可以根据已成功上传的分片信息,从上次中断的地方继续上传,避免重复上传已上传的部分,节省时间和资源。
  • 内存占用低:一次性将大文件加载到内存可能导致内存溢出,而分片上传只需加载单个分片,减少了对内存的压力,适应内存有限的环境。
  • 服务器负载平衡:使用分片上传可以将上传请求分散到多个服务器,降低单一服务器的负载压力,提高整体系统的稳定性和吞吐量。
  • 灵活性:分片上传允许用户在任意时间暂停、取消或恢复上传过程,提供了更好的用户体验和操作灵活性。

常有人疑惑,为何其他网站上传速度很快,而自己的网速相同却较慢。这往往与是否进行分片上传有关。虽然直接上传一个文件开发更快,但分片上传会增加前端和服务端的工作成本。接下来,我们将探讨如何实现分片上传:

// 定义分片大小(以字节为单位) // 1MB
// 将文件切割为多个分片
function sliceFileIntoChunks(file, chunkSize = 1024 * 1024) {
  const fileSize = file.size;
  const chunks = Math.ceil(fileSize / chunkSize);
  const fileChunks = [];
  let start = 0;
  let end = chunkSize;
  for (let i = 0; i < chunks; i++) {
    const chunk = file.slice(start, end); // slice 可以把文件按照需要的大小分割
    fileChunks.push(chunk);
    start = end;
    end = start + chunkSize;
  }
  return fileChunks;
}

获取上传进度(此需求回到了【多文件上传展示一个进度条】的主题),具体实现如下:

const files = sliceFileIntoChunks(originFile).map((item) => ({file: item, status: UPLOAD_TYPE_MAP.UPLOADING}));
// ... 忽略
const timer = setInterval(() => {
  const eachProgress = 100 / files.length; // 计算单个的比例
  const complateUpload = files.filter((item) => item.status === UPLOAD_TYPE_MAP.COMPLATE);
  const upperLimit = (complateUpload.length + 1) * eachProgress;
  const maxProgress = upperLimit > 100 ? 100 : upperLimit; // 计算出当前最大进度
  if(progress < maxProgress) {
    progress++; // 边界控制
  }
  if(maxProgress === 100) {
    progress = 100;
    clearInterval(time);
  }
}, 500);

需要注意一点,必须告知服务端切片索引,以便服务端后续合并切片。如果按大小(如 1MB)切割,上传完成后需发送通知告知服务端合并切片。如果按份额(如 10 等份)切割,上传完成的请求中无需额外通知服务端,服务端也可判断上传状态。

结语

至此,关于 JavaScript 获取上传进度的几种方式的文章介绍完毕。更多相关 JavaScript 获取上传进度的内容,请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章