Загрузка файла внешнего интерфейса и экспорт в Excel

внешний интерфейс

предисловие

В своей предыдущей работе я столкнулся с необходимостью экспортировать Excel.Я разобрался здесь.Вообще, при экспорте excel фронтенд в основном делится на два типа.Первый -бэкенд возвращает бинарный поток данных , а другой создается непосредственно внешним интерфейсом (например, экспорт шаблона). Способ экспорта может использовать отправку формы, загрузку тега, window.open для открытия нового окна.Наиболее часто используемым должен быть тег a.

текст

Здесь в основном два способа загрузки файлов, и решение проблемы искаженных символов, когда бэкенд возвращает поток бинарных данных, и сбой сети, вызванный большим объемом данных.

Новое: добавьте чистый интерфейс для чтения данных и их анализа здесь.

Важно: здесь необходимо использовать два плагина:

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

Во-первых, нам нужно получить файл для анализа, мы можем использовать компонент загрузки в element-ui (просто пример, особых ограничений нет, пока мы можем получить загруженный файл), а затем получить его файл в метод загрузки httprequest,
Затем полученный файл подставляем формальным параметром в метод парсинга и все

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)
 },

В методе вы можете получить разобранные данные в виде пар ключ-значение, а затем оперировать ими самостоятельно. следует отметить, что при импорте объема данных, когда он слишком велик, например, 3-5 Вт, это может занять много времени, и это необходимо контролировать самостоятельно.

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-формата

Шаг 1: Установите файловую заставку и 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>

Шаг 3: Привяжите метод вызова кнопки экспорта

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)
    })
```

Эпилог

Функция не сложная, следует отметить, что преобразование бинарного потока, и проблема в том, что адрес метки слишком длинный и сеть не работает.