likes
comments
collection
share

【纯前端】如何实现excel的导入导出?

作者站长头像
站长
· 阅读数 13

背景

    提到excel的导入导出,相信不少童鞋第一反应就是导入利用上传组件获取到文件然后处理成相应的格式再调一下后端接口,导出就更简单了,调一下后端大佬的接口就行啦。当然,这是最常见的场景,但有些需求场景如获取到excel文件渲染成可编辑的表格,后端需要的只是编辑后的数据,又或者下载的是频繁更改后的表格数据,这些场景的导入导出不需要频繁更新数据库,因此咱们前端学会自给自足可能会更好。下面就一起看看具体的实现吧!

方案

    在实际的工作中采用的是js-xlsx,选用它的原因如下:功能强大、支持格式多、兼容性好、文档详细、开源并持续维护,由官网可以知道js-xlsx支持的读写类型及常用的数据转换方法如下:

文件读取类型

类型预期输入
"base64"Base64编码类型字符串
"binary"二进制字符串(字节n是data.charCodeAt(n))
"string"JS字符串(仅适用于UTF-8文本格式)
"buffer"nodejs的buffer类型
"array"数组
"file"将被读取的文件路径(仅限nodejs)

常用方法

  • sheet_to_*函数接受一个工作表和一个可选的options对象,主要是将excel文件转化为对应的数据格式,一般导入excel文件的时候使用
  • *_to_sheet函数接受一个数据对象和一个可选的options对象,主要是将数据格式转化为excel文件,一般导出文件的时候使用
  • sheet_add_*函数接受工作表、数据和可选选项。主要用途是更新一个现有的工作表对象
方法名用途
XLSX.utils.aoa_to_sheet(aoa, opts)数组转工作表,导出可用
XLSX.utils.json_to_sheet(aoo, opts)对象数组转工作表 ,导出可用
XLSX.utils.table_to_sheet(elt, opts)表格元素转工作表,导出可用
XLSX.utils.book_new()创建一个空工作簿,导出可用
XLSX.utils.book_append_sheet(workbook, worksheet, sheet_name)将工作表添加到工作簿,第三个参数指定所需的工作表名称。
XLSX.utils.table_to_book(elt, opts)基于工作表生成最小工作簿
XLSX.utils.sheet_to_html(ws, opts)工作表转html,导入解析可用
XLSX.utils.sheet_to_json(ws, opts)工作表转JS对象数组,导入解析可用

实现

    在了解了js-xlsx的常用方法跟支持入参格式后,接下来excel文件的导入导出就简单得多了,ps以下的实现主要是基于vue3+ts,其他用法请自行查阅官网示例

1.安装xlsx

pnpm install xlsx
// 也可以按官网例子通过cdn下载
npm init vue @latest--sheetjs - vue--default
cd sheetjs - vue
npm i
npm i--save <https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz>
npm run dev

2.导入导出结果展示

搞个简陋的栗子试试看吧~

3.实现代码

3.1 html部分

    <template>
      <table>
        <thead>
          <th>Name</th>
          <th>Index</th>
        </thead>
        <tbody>
          <tr v-for="(row, idx) in rows" :key="idx">
            <td>{{ row.Name }}</td>
            <td>{{ row.Index }}</td>
          </tr>
        </tbody>
        <tfoot>
          <td colSpan="{2}">
            <button @click="exportFile">Export XLSX</button>
          </td>
          <td colSpan="{2}">
            <input accept=".xlsx,.xls" type="file" @change="handleClick" />
          </td>
        </tfoot>
      </table>
    </template>

3.2 导出文件

思路如下:

  1. 将JS对象数组转换为工作表
  2. 创建一个空工作簿
  3. 将工作表添加到工作簿
  4. 利用writeFileXLSX导出工作表
        import { writeFileXLSX , utils } from "xlsx";
        /\* 对象数组导出为excel文件 \*/
        const exportFile=()=>{
        const ws = utils.json\_to\_sheet(rows.value)
        const wb = utils.book\_new();
        utils.book\_append\_sheet(wb, ws, "Data");
        writeFileXLSX(wb, "SheetJSVueAoO.xlsx");
        }

3.3 excel转对象数组

思路如下:

  1. 在FileReader的onload事件获取对应文件
  2. 使用xlsx.read解析数据
  3. 使用xlsx.utils.sheet_to_json将数据转化为对象数组
    import { read, utils } from "xlsx";
    import type { Sheet2JSONOpts } from "xlsx";
    const excelToJson = (files: File, sheetOption?: Sheet2JSONOpts) => {
    if (!files) {
      return Promise.resolve([]);
    } else if (!/\.(xls|xlsx)$/.test(files.name.toLowerCase())) {
      return Promise.resolve([]);
    }
    type NormalObj = {
      [key: string]: string | number;
    };
    return new Promise((resolve) => {
      /** 读取excel */
      const fileReader = new FileReader();
      fileReader.onload = (ev: ProgressEvent<FileReader>) => {
        if (ev?.target?.result === null) {
          return Promise.resolve([]);
        }
        const workbook = read(ev?.target?.result, {
          type: "binary",
        });
        const wsname = workbook.SheetNames[0];
        /**raw为false不格式化数据,有格式化数据需求可以设为true */
        const jsonOptions = Object.assign(
          { raw: false },
          sheetOption
        ); /** 转换成json的数据 */
        const ws = utils.sheet_to_json(
          workbook.Sheets[wsname],
          jsonOptions
        ) as NormalObj[];
        resolve(ws);
      }; /** 开始读取文件 */
      fileReader.readAsBinaryString(files);
    });
    };

3.4 获取导入文件函数

const handleClick = async (e) => {
  const files = e.target.files;
  const rawFile = files[0];
  if (!rawFile) return;
  // 从excel文件读取的对象数组
  const data = await excelToJson(rawFile);
};

总结

    至此,利用js-xlsx导入导出excel文件的实现告一段落,其中导入也可以使用elemen-plus的上传组件,只需将获取到的文件利用xlsx.utils的对应方法转化为相应的格式即可~