使用 Axios 下载文件并更新进度条

使用 Axios 下载文件并更新进度条

使用axiosonDownloadProgress回调函数实现下载文件,并更新下载进度条。

示例代码

import { ElMessage } from "element-plus";
import axios from "axios";
import type { AxiosResponse, AxiosProgressEvent } from "axios";
import { baseUrl } from "@/utils/baseUrl";
import type { FileInfo } from "@/interfaces";

const apiPrefix = "/api-file";

type ProgressCallback = (file: FileInfo, progress: number) => void;

// 防抖函数
function debounce<ProgressCallback extends (...args: any) => any>(
  func: ProgressCallback,
  wait: number
): ProgressCallback {
  let startTime = Date.now();
  return function (this: any, ...args: Parameters<ProgressCallback>) {
    if (Date.now() - wait >= startTime) {
      func.apply(this, args);
      startTime = Date.now();
    }
  } as ProgressCallback;
}

export async function downloadFile(
  file: FileInfo,
  token: string | null,
  updateProgressBar: ProgressCallback
): Promise<void> {
  try {
    const updateProgress = debounce(updateProgressBar, 500); // 500ms 防抖间隔时间
    const response: AxiosResponse = await axios({
      url: `${baseUrl}${apiPrefix}/files/${file.id}/`,
      method: "GET",
      responseType: "blob", // 接收二进制数据
      headers: token ? { Authorization: "Bearer " + token } : {},
      onDownloadProgress: (progressEvent: AxiosProgressEvent) => {
        const total = progressEvent.total;
        const current = progressEvent.loaded;
        if (total) {
          const percentage = Math.floor((current / total) * 100);
          updateProgress(file, percentage);
        }
      },
    });

    if (response.headers["content-type"].startsWith("application/json")) {
      const resCode = response.data.code;
      if (resCode !== 0) {
        ElMessage.warning(response.data.msg);
      }
    } else {
      // 处理文件名,从响应头的 content-disposition 中获取
      let filename = "";
      const disposition = response.headers["content-disposition"];
      const disposition = response.headers["content-disposition"];
      if (disposition) {
        const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        const matches = filenameRegex.exec(disposition);
        if (matches && matches[1]) {
          filename = matches[1].replace(/['"]/g, ""); // 去除引号
        }
      }
      // 处理下载的文件
      const urlBlob = window.URL.createObjectURL(new Blob([response.data]));
      const link = document.createElement("a");
      link.style.display = "none";
      link.href = urlBlob;
      link.download = filename; // 指定下载后的文件名,防止跳转
      document.body.appendChild(link);
      link.click();
      window.URL.revokeObjectURL(urlBlob);
      document.body.removeChild(link);
    }
  } catch (error) {
    console.error("Error downloading the file:", error);
    ElMessage.warning("下载文件失败,请稍后再试");
  }
}

/**
 * 更新页面中的进度条
 */
const updateProgressBar = (file: FileInfo, percentage: number) => {
  file.downloading = true;
  console.log(`Download progress: ${percentage}%`);
  file.downloadingProgress = percentage;
};

代码讲解

1. 使用 axios 进行文件下载

使用axios发送了一个GET请求来下载文件,并指定了responseTypeblob,以确保接收到的文件是二进制数据,并在请求头中传入 jwt token。

const response: AxiosResponse = await axios({
  url: `${baseUrl}${apiPrefix}/files/${file.id}/`,
  method: "GET",
  responseType: "blob", // 接收二进制数据
  headers: token ? { Authorization: "Bearer " + token } : {},
});

2. 实现下载进度的更新

通过axiosonDownloadProgress回调函数,可以获取下载的进度。回调函数在文件下载过程中不断触发,提供了loadedtotal两个属性,分别代表已下载的字节数和总字节数。

onDownloadProgress: (progressEvent: AxiosProgressEvent) => {
  const total = progressEvent.total;
  const current = progressEvent.loaded;
  if (total) {
    const percentage = Math.floor((current / total) * 100);
    updateProgress(file, percentage);
  }
};

为了防止进度条更新过于频繁导致的性能问题,使用了一个简单的防抖函数debounce,将更新进度的频率限制为 500 毫秒。
调用downloadFile函数时,需要传入updateProgressBar函数,在该函数中实现更新页面进度条操作。

/**
 * 更新页面中的进度条
 */
const updateProgressBar = (file: FileInfo, percentage: number) => {
  file.downloading = true;
  console.log(`Download progress: ${percentage}%`);
  file.downloadingProgress = percentage;
};

3. 文件处理与下载

在下载完成后,根据响应头中的Content-Disposition获取文件名,然后创建了一个 URL 对象,并利用a标签触发文件下载。

const urlBlob = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.style.display = "none";
link.href = urlBlob;
link.download = filename; // 指定下载后的文件名,防止跳转
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(urlBlob);
document.body.removeChild(link);
posted @ 2024-08-14 18:47  守望人间  阅读(1189)  评论(0)    收藏  举报