axios分段下载文件

529 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

 之前遇到一个文件过大下载链接断开的问题,刚开始增加超时时间,效果并不好,后来经过各方面研究,上传都可以分段上传,下载也可以,所以和后端配合,实现了分段下载功能,现在把axios如何实现分段下载逻辑,总结成方法,以便以后参考。

<template></template>

<script>
export default {
  name: "download",
  data() {
    return {
      percentage: -1, // 下载进度
    };
  },
  methods: {
    // 分段下载需要后端配合
    download() {
      // 下载地址
      const url = "...";
      const chunkSize = 1024 * 1024 * 100; // 单个分段大小,这里测试用100M
      let filesTotalSize = chunkSize; // 安装包总大小,默认100M
      let filesPages = 1; // 总共分几段下载
      let filesCurrentPage = 0; // 第几段
      let contentList = []; // 文件流数组
      let sentAxios = (num) => {
        let rande = chunkSize;
        if (num) {
          rande = `${(num - 1) * chunkSize + 2}-${num * chunkSize + 1}`;
        } else {
          // 第一次0-1方便获取总数,计算下载进度,每段下载字节范围区间
          rande = "0-1";
        }
        let headers = {
          range: rande,
        };
        this.axios({
          method: "get",
          url: url,
          data: {},
          headers: headers,
          responseType: "blob",
          onDownloadProgress: (a) => {
            if (!num) {
              return;
            }
            var percent = parseInt(
              100 * ((a.loaded + 2 + (num - 1) * chunkSize) / filesTotalSize)
            );
            this.$nextTick(() => {
              this.percentage = percent;
              if (percent == 100) {
                // 1秒后隐藏进度显示
                setTimeout(() => {
                  this.percentage = -1;
                }, 1000);
              }
            });
          },
        })
          .then((response) => {
            if (response.status == 200 || response.status == 206) {
              //检查了下才发现,后端对文件流做了一层封装,所以将content指向response.data即可
              const content = response.data;
              // 获取文件总大小,方便计算下载百分比
              filesTotalSize = response.headers["content-range"].split("/")[1];
              // 计算总共页数,向上取整
              filesPages = Math.ceil(filesTotalSize / chunkSize);
              contentList.push(content); // 文件流数组
              // 递归获取文件数据
              if (filesCurrentPage < filesPages) {
                filesCurrentPage++;
                sentAxios(filesCurrentPage);
                return;
              }
              //构造一个blob对象来处理数据
              const reader = new FileReader();
              reader.readAsText(response.data);
              reader.onload = () => {
                try {
                  // 如果返回的报文不是数据流,而是json,比如登录失效,执行下面逻辑
                  const res = JSON.parse(reader.result);
                  if (res.code === 401) {
                    // ...处理逻辑
                  }
                } catch (err) {
                  const blob = new Blob(contentList);
                  // 文件名称
                  const fileName = response.fname;
    
                  //对于<a>标签,只有 Firefox 和 Chrome(内核) 支持 download 属性
                  //IE10以上支持blob但是依然不支持download
                  if ("download" in document.createElement("a")) {
                    //支持a标签download的浏览器
                    const link = document.createElement("a"); //创建a标签
                    link.download = fileName; //a标签添加属性
                    link.style.display = "none";
                    link.href = URL.createObjectURL(blob);
                    document.body.appendChild(link);
                    link.click(); //执行下载
                    URL.revokeObjectURL(link.href); //释放url
                    document.body.removeChild(link); //释放标签
                  } else {
                    //其他浏览器
                    navigator.msSaveBlob(blob, fileName);
                  }
                  this.$message.success("下载完成!");
                }
              }
            } else {
              this.$message.error("下载失败!");
            }
          })
          .catch(function (error) {
            console.log(error);
          });
      };
      // 第一次获取数据方便获取总数
      sentAxios(filesCurrentPage);
    },
  },
};
</script>