• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

前端实现下载进度条百分比实时显示

武飞扬头像
轻舞飞扬_
帮助2

工作中经常会遇到下载文件的需求,如果一般的大小还可以很快的下载下来,但是如果报表类的文件,一般都会比较大,这时候就会等很久才可以下载完成,但是用户可能并不知道目前下载的情况,可能会以为没有下载成功,频繁点击,所以网上搜索了一下,整理了目前比较常用的显示下载进度的方式,目前是使用VUE elementUi Axios 实现的

一般调用接口有两种下载方式:

第一种,直接访问**[*服务器的文件地址,自动下载文件

第二种 ,服务器返回blob文件流,再对文件流进行处理和下载。

一般小文件后端会使用第一种方式,大文件会采取文件流的方式进行下载

这个是我公司的下载接口,可以看到已经是很慢的了,体验并不好,所以加一个能实时显示进度的功能,这样用户也能有感知。

学新通 具体步骤:

1.封装一个调用接口的公用方法:

// 新建一个index.js文件或写在comments.js公共文件中
import axios from "axios";  // 没有下载的话可以直接装一个   npm i axios
export function downLoadAll(params) {
  let downProgress = {};
  let uniSign = new Date().getTime()   ""; // 可能会连续点击下载多个文件,这里用时间戳来区分每一次下载的文件
  const url = process.env.VUE_APP_BASE_API   params.url; // 我的项目是需要本地配置不同环境的地址,如果能够直接拿到,就可以不用拼接
  axios({
    url: url,
    method: "post",  // 调用类型依据后端接口调用为准
    data: params.data, // 接口入参  post:data;get:params
    responseType: "blob", // 响应类型  
    headers: { Authorization: "Bearer "   getToken() },  //这里需要拼接token 否则会无法请求,我这里是调用getToken方法拿到的,以前就配好了
    onDownloadProgress(progress) {  // onDownloadProgress 监听下载进度的方法
      downProgress = Math.round((100 * progress.loaded) / progress.total); // progress对象中的loaded表示已经下载的数量,total表示总数量,这里计算出百分比。注意这里一定需要后端接口返回 Content-Length,没有的话是拿不到的
      store.commit("downFiles/SET_PROGRESS", {
        path: uniSign,
        progress: downProgress,
      }); // 将此次下载的文件名和下载进度组成对象再用vuex状态管理
    },
  })
    .then((res) => {
      // 文件流传输完成后,开启文件下载
      if (params.downLoadName) {
        jsFileDownLoad(res.data, params.downLoadName   ".xlsx"); // jsFileDownLoad是用来下载文件流的,下载插件:npm i js-file-download,import引入:import jsFileDownLoad from 'js-file-download'
      } else {
        jsFileDownLoad(res.data, "下载文件.xlsx"); // 如果没有文件名字,就给个默认文件名
      }
    })
    .catch((e) => {
      console.log(e)
      params.that.$message.error("该文件无法下载"); // 报错处理.params.that由于拿不到this,所以传入的
    });
}

一定要有Content-Length!!!

学新通

2.vuex封装

const state = {
  progressList: [], // 文件下载进度列表
};
const mutations = {
  SET_PROGRESS: (state, progressObj) => {
    // 修改进度列表
    if (state.progressList.length) {
      // 如果进度列表存在
      if (state.progressList.find((item) => item.path == progressObj.path)) {
        // 前面说的path时间戳是唯一存在的,所以如果在进度列表中找到当前的进度对象
        state.progressList.find(
          (item) => item.path == progressObj.path
        ).progress = progressObj.progress; // 改变当前进度对象的progress
      }
    } else {
      state.progressList.push(progressObj); // 当前进度列表为空,没有下载任务,直接将该进度对象添加到进度数组内
    }
  },
  DEL_PROGRESS: (state, props) => {
    state.progressList.splice(
      state.progressList.findIndex((item) => item.path == props),
      1
    ); // 删除进度列表中的进度对象
  },
};

export default {
  namespaced: true, // 一定要加
  state,
  mutations,
};

// 这里是使用module 子模块,需要在根文件的state里面引入使用
import downFiles from "./modules/downFiles"; 
moudle:{
    downFiles
}

3.生成一个mixins文件,因为许多地方要用,所以直接使用混入比较方便,而使用混入比较方便

// 用到了element的消息提示Notification 组件
import { mapState } from "vuex";
export const mixins = {
  computed: {
    ...mapState({
      progressList: (state) => state.downFiles.progressList,
    }),
  },
  data() {
    return {
      notify: {}, // 用来维护下载文件进度弹框对象
    };
  },
  watch: {
    // 监听进度列表
    progressList: {
      handler(n) {
        let data = JSON.parse(JSON.stringify(n));
        data.forEach((item) => {
          const domList = [...document.getElementsByClassName(item.path)];
          if (domList.find((i) => i.className === item.path)) {
            // 如果页面已经有该进度对象的弹框,则更新它的进度progress
            if (item.progress)
              domList.find((i) => i.className === item.path).innerHTML =
                item.progress   "%";
            if (item.progress === null) {
              // 此处容错处理,如果后端传输文件流报错,删除当前进度对象
              this.$store.commit("downFiles/DEL_PROGRESS", item.path);
              this.$notify.error({ title: "错误", message: "文件下载失败!" });
            }
          } else {
            // 如果页面中没有该进度对象所对应的弹框,页面新建弹框,并在notify中加入该弹框对象,属性名为该进度对象的path(上文可知path是唯一的),属性值为$notify(element ui中的通知组件)弹框对象
            this.notify[item.path] = this.$notify.success({
              dangerouslyUseHTMLString: true,
              customClass: "progress-notify",
              message: `<p style="width: 150px;line-height: 13px;">文件下载进度<span class="${item.path}" style="float: right">${item.progress}%</span></p>`, // 显示下载百分比,类名为进度对象的path(便于后面更新进度百分比)
              showClose: true,
              duration: 0,
            });
          }
          if (item.progress === 100) {
            // 如果下载进度到了100%,关闭该弹框,并删除notify中维护的弹框对象
            // this.notify[item.path].close()
            // 上面的close()事件是异步的,直接delete this.notify[item.path]会报错,利用setTimeout,将该操作加入异步队列
            setTimeout(() => {
              delete this.notify[item.path];
            }, 1000);
            this.$store.commit("downFiles/DEL_PROGRESS", item.path); // 删除caseInformation中state的progressList中的进度对象
          }
        });
      },
      deep: true,
    },
  },
};

4.页面调用

import { downLoadAll } from 导入方法
confirmExport() {    // 点击确认下载
      try {
        const data = {
          参数: 参数值,
        };
        const downLoadName = "文件下载"
        const params = {
          url: url,
          data,
          downLoadName,
          that: this,
        };
        downLoadAll(params);
      } catch (error) {
        console.log(error);
      }
    },

完成,效果如下:

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbbacc
系列文章
更多 icon
同类精品
更多 icon
继续加载