在 vue 中实现文件下载功能

639 阅读1分钟

前言

记录一下文件下载的两种方式。一种是通过后端返回字节流来下载文件,另一种是通过插件前端自行下载。

方便自己以后CV。

接口返回字节流

  • template
<template>
  <div>
    <el-button v-loading="isLoading" @click="downloadExcel"
      >click me</el-button
    >
  </div>
</template>
  • script
<script>
import TestApi from '@/api/test';

export default {
  components: {},
  data() {
    return {
      isLoading: false
    };
  },
  created() {},
  methods: {
    async downloadExcel() {
      const params = {
        surveyId: 1732,
        userId: 520400
      };
      const fileInfo = {
        fileName: '测试下载文件',
        type: 'xlsx'
      };
      this.isLoading = true;
      const res = await TestApi.downloadExcel(params, fileInfo);
      this.isLoading = false;
      this.$message({
        type: res.code === 1 ? 'success' : 'error',
        message: res.message
      });
    }
  }
};
</script>
  • api
import request from '@/utils/request.js';

export default class TestApi {
  /**
   * Excel 文件导出
   * @param {Object} params 入参
   * @param {fileInfo} 导出文件名称及类型
   * @returns
   */
  static async downloadExcel(params, fileInfo) {
    const response = await request({
      method: 'post',
      url: 'api/test/downloadExcel',
      params,
      responseType: 'blob',
    });

    const { status, data } = response
    if (status !== 200) {
      return {
        code: 0,
        message: '下载失败,请稍后重试'
      }
    }
    
    try {
      // const contentType = response.headers['content-type'];
      // const blob = new window.Blob([data], {
      //  type: contentType
      // });
      const a = document.createElement('a');
      // const href = window.URL.createObjectURL(blob); //创建下载的链接
      const href = window.URL.createObjectURL(data); //创建下载的链接
      a.href = href;
      const { fileName, type } = fileInfo;
      a.download = `${fileName}.${type}`;
      a.click();
      window.URL.revokeObjectURL(href); // 释放掉blob对象
      return {
        code: 1,
        message: '下载成功'
      }
    } catch (err) {
      return {
        code: 0,
        message: err
      }
    }
  }
}
  • 其中 api 返回的 response 见下图

image.png

前端通过插件自行下载

现在下载的插件很多,每个插件都有自己的使用方式。

这里我举一下老项目中使用的js-xlsx

// 将 workbook 转为blob 对象
function _wb2blob(workbook) {
  const wopts = {
    // 要生成的文件类型
    bookType: 'xlsx', 
    // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
    bookSST: false, 
    type: 'binary'
  };

  const wbout = XLSX.write(workbook, wopts);

  // 字符串转ArrayBuffer
  function s2ab(s) {
    const buf = new ArrayBuffer(s.length);
    const view = new Uint8Array(buf);
    for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
    return buf;
  }

  const blob = new Blob([s2ab(wbout)], {
    type: 'application/octet-stream'
  });
  return blob;
}

// 通过 blob 对象下载 excel
function _openDownloadDialog(b, saveName) {
  if (typeof b === 'object' && b instanceof Blob) {
    const url = URL.createObjectURL(b);
    const aLink = document.createElement('a');
    aLink.href = url;
    aLink.download = saveName || '';
    let event;
    if (window.MouseEvent) event = new MouseEvent('click');
    else {
      event = document.createEvent('MouseEvents');
      event.initMouseEvent(
        'click',
        true,
        false,
        window,
        0,
        0,
        0,
        0,
        0,
        false,
        false,
        false,
        false,
        0,
        null
      );
    }
    aLink.dispatchEvent(event);
  }
}

// 通过 id 获取 Element 的 Table
function _getElementTableStrById(id) {
  const wbtable = document.createElement('table');
  const tableHeader = document.querySelector(`#${id} .el-table__header thead`);
  const tableBody = document.querySelector(`#${id} .el-table__body tbody`);
  // 创建节点的拷贝,并返回该副本
  const headerClone = tableHeader.cloneNode(true);
  const bodyClone = tableBody.cloneNode(true);
  wbtable.appendChild(headerClone);
  wbtable.appendChild(bodyClone);
  return wbtable
}

// 通过传入的 tableStr 创建 table
function _getDefinedTableStr(tableStr) {
  const wbtable = document.createElement('table');
  wbtable.innerHTML = tableStr;
  return wbtable
}

/**
 * 下载
 * @param {String} id id 为 el-table 绑定 id
 * @param {String} fileName 下载文件名称
 * @param {String} tableStr  table innerHtml 
 */
export function downloadExcel({ id, tableStr, fileName }) {
  /**
   * id 和 tableStr 传一个即可
   * id 表示下载的为 el-table
   * tableStr 表示下载的自定义表格,需要传一个字符串,格式为`<table>${theadStr}${thbodyStr}</table>`;
   */
  const wbTable = tableStr ? _getDefinedTableStr(tableStr) : _getElementTableStrById(id);

  // 1-创建工作薄
  const workbook = XLSX.utils.book_new();

  // 2-创建工作表
  const sheet = XLSX.utils.table_to_sheet(wbTable);
  // 设置单元格宽度
  sheet['!cols'] = new Array(Object.keys(sheet).length).fill({ wpx: 120 });

  // 3-将 sheet 添加到 workbook 中
  XLSX.utils.book_append_sheet(workbook, sheet, 'sheet1');

  // 4-下载
  const blob = _wb2blob(workbook);
  _openDownloadDialog(blob, `${fileName}.xlsx`);
}

补充

后端返回字节流这块的代码我自己跑过,无报错。

通过插件这个只是大概的思路,具体还要根据项目分析。

最后


  • 文章是自己手敲,是对工作日常的总结,如有错误之处,敬请指正
  • 如果遇到什么问题就留言吧,能解决大家帮忙一起解决一下