前端文件下载导出Excel

18,888 阅读4分钟

前言

之前在工作中,有遇到过需要导出Excel的需求, 这里整理了一下,一般前端在导出excel时, 多分为两种, 一种是后端返回二进制数据流, 另一种是直接纯前端生成(如导出一个模板). 而导出的方式可以使用form表单提交, a标签下载, window.open打开新窗口. 最常用的应该就是a标签了.

正文

这里主要讲两种方式的文件下载方法, 和后端返回二进制数据流时,产生乱码,及数据量过大导致网络失败的解决方法.

新增: 这里添加一个纯前端读取数据并解析

重点 : 这里需要用到的插件有两个,

import FileSaver from 'file-saver'
import XLSX from 'xlsx'

首先 我们需要拿到要解析的文件, 可以使用element-ui里的upload组件(只是举例,具体没有限制,只要拿得到上传的文件), 然后再httprequest那个上传方法里获取它的file,
然后, 我们把拿到的file作为形参放到解析的方法里,就可以了

async uploadFile(file) {
   const _file = file
   const fileReader = new FileReader()
   fileReader.onload = (ev) => {
     try {
       const data = ev.target.result
       const workbook = XLSX.read(data, {
         type: 'binary'
       })
       for (const sheet in workbook.Sheets) {
         // 循环读取每个文件
         const sheetArray = XLSX.utils.sheet_to_json(workbook.Sheets[sheet])
         // 若当前sheet没有数据,则continue
         if (sheetArray.length === 0) {
           continue
         }
         // arr 可以是需要存放数据的数组
         const arr = []
         // 这里可以打印下, 可以拿到键值对形式的解析数据, 之后就可以自己去操作了
         sheetArray.forEach(e => {
           const rowTable = {}
           for (const item in e) {
             if (item === '货号') {
               rowTable['productNo'] = e[item]
             } else if (item === '条码') {
               rowTable['barCode'] = e[item]
             } else {
               rowTable['safeQty'] = e[item]
             }
           }
           arr.push(rowTable)
         })
     } catch (e) {
       this.$message.warning('文件解析出错!')
     }
   }
   fileReader.readAsBinaryString(_file)
 },

在方法中, 可以拿到键值对形式的解析数据, 之后就可以自己去操作了, 方法里的arr是定义操作的数组, 这个可以自己随意去操作. 不过需要注意的是, 在导入数据量过大的时候, 比如3-5W条, 可能会久一些, 这个需要自己去控制了

1.纯前端生成EXcel, 这里更新一下, 增加导出xlsx格式的方法

这种方法用的不多,一般文件下载还是主要依赖于后端返回二进制数据流的形式实现. 这种方法一般用于json格式数据导出模板 话不多说,先上代码

```
 let str = ''
  const jsonData = [{ '仓库代码': '', '货号': '', '尺码': '', '条码': '', '切货数量': '', '折扣': '' }]
  console.log(jsonData)
  for (var k in jsonData[0]) {
    str += k + ','
  }
  str = str.slice(0, str.length - 1) + '\n'
  console.log(str)
  // 增加\t为了不让表格显示科学计数法或者其他格式
  for (let i = 0; i < jsonData.length; i++) {
    for (const item in jsonData[i]) {
      str += `${jsonData[i][item] + '\t'},`
    }
    str += '\n'
  }
  // encodeURIComponent解决中文乱码
  const uri = 'data:application/vnd.ms-excel;charset=utf-8,\ufeff' + encodeURIComponent(str)
  // 通过创建a标签实现
  const link = document.createElement('a')
  link.href = uri
  // 对下载的文件命名
  link.download = '订单模板.xls'
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
```

导出xlsx格式

第一步: 安装 file-saver 和 xlsx

 npm i file-saver xlsx -s
第二步: 在要用的组件用中引入
  import FileSaver from 'file-saver'
 import XLSX from 'xlsx'
// 为表格绑定一个id, 特别注意
<el-table
   id="table"
   :data="tableData"
   style="width: 100%">
   <el-table-column
     prop="date"
     label="日期"
     width="180">
   </el-table-column>
   <el-table-column
     prop="name"
     label="姓名"
     width="180">
   </el-table-column>
   <el-table-column
     prop="address"
     label="地址">
   </el-table-column>
 </el-table>

第三步:绑定导出按钮的调用方法

getXlsx() {
             let wb = XLSX.utils.table_to_book(document.querySelector('#table'));
             /* #table 就是表格的id */
             let wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: true, type: 'array'});
             try {
                 FileSaver.saveAs(new Blob([wbout], {type: 'application/octet-stream'}), '导出数据.xlsx');
             } catch (e) {
                 if (typeof console !== 'undefined')
                     console.log(e, wbout)
             }
             return wbout


         },

2.后端返回二进制数据流,生成Excel

对于导出数据而言,返回二进制流文件是最常见的, 而前端打开链接下载excel文件一般有三种方式, 
第一种是form表单方式 , 也是同步下载方式,直接下载. 这种方式的优点在于不需要对返回数据进行转换操作, 浏览器会自动同步解析. 但缺点是无法对返回结果进行操作, 如:一般工作中需要对请求进行鉴权, 这个时候,form表单方式下载是无法在请求头中带上token的, 后端只能通过从cookie中获取.

第二种是a标签下载方式, 将返回结果处理成一个新链接, 通过创建a标签打开, 这种方式的优点在于内部请求不需要对鉴权做多余处理, 也可以拿到返回结果进行操作,
缺点在于不注意之间会产生乱码,及数据量太大导致网络失败. 
使用blob容器可以解决数据量大导致网络失败的问题, 
而乱码问题在二进制流前拼接字符串'\ufeff'即可
```
 this.$axios({
      method: params.method,
      url: params.url,
      data: params.data,
      responseType: 'blob'  // 指明返回格式, 这里注明一下, 如果导出EXCEL为[object blob]的话, 可以把这段responseType:'blob' 注释掉试一下.  
    }).then(res => {
      console.log(res) // 返回结果
      // 这里尤其需要注意, '\ufeff' 用于解决乱码问题, blob可以解决数据量大导致网络失败.
      const blob = new Blob(['\ufeff' + res.data], { type: 'text/csv;charset=utf-8' })
      const url = window.URL.createObjectURL(blob)
      // 通过创建a标签实现
      const link = document.createElement('a')
      link.href = url
      // 对下载的文件命名, 如果后端返回名称出现乱码, 需要后端编码一下.
      link.download = decodeURI(res.headers['content-disposition'].split('=')[1]) || '发货单导出数据表.csv'
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
    })
```


结语

功能不难,需要注意的是二进制流的转换, 和a标签地址太长导致网络失败的问题就行.