使用XLSX来解析Excel的内容

7,403 阅读3分钟

工作中经常遇到让前端来识别Excel的内容,以此来进行校验,那么首先如何获取Excel的内容就是一个问题,百度了一下发现用SheetJS开源的JS-XLSX这个工具库居多。它支持xls、xlsx、csv等多种表格格式文件的解析。 官方github:github.com/SheetJS/js-…

Demo图例: alt

最后期望获取的数据结构: alt

如何使用XLSX

首先,官网这里的文件只需要用dist目录下的 xlsx.core.min.js 就足够了;xlsx.core.min.js主要是包含了基本的识别解析功能,xlsx.full.min.js则是包含了所有功能模块。然后在所在项目引入即可。

下面演示的案例是在原生JS下写的,所以用的script标签引入的,若是Vue等框架项目可以用 npm 下载引入 XLSX即可。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <input type="file" onchange="changes(event)" />
  </body>
</html>
// 引入本地的xlsx工具库
<script src="./xlsx.core.min.js"></script>

<script>
  const file = document.querySelector('input');
  function changes(e) {
    if (e.target.files.length > 0) {
      // 获取当前的文件名后缀进行判断是否可以解析
      const fileName = e.target.files[0].name;
      const fileArr = fileName.split('.');
      const fileSuffix = fileArr[fileArr.length - 1];
      if (fileSuffix === 'xlsx' || fileSuffix === 'xls') {
        // 解析数据
        fileToJson(e.target.files[0], (sheets) => {
          console.log('获取到的表格数据', sheets);
        });
      } else {
        console.log('不支持该格式的解析');
      }
    } else {
      console.log('请选择文件上传');
    }
  }
</script>

上传文件后,要创建一个FileReader对象,该对象中的方法可以读取文件内容,但是需要经过XLSX解析编译才可以阅读。

FileReader 接口参考地址:developer.mozilla.org/zh-CN/docs/…

alt

根据FileReader对象返回的结果,XLSX通过read方法来解析读取,返回一个WorkBook对象。 read方法接受两个参数,参数一是FileReader返回的数据,参数二(可选)是一个配置对象,

alt

常用的有type和cellDates等字段;type主要取值如下: base64: 以base64方式读取; binary: BinaryString格式(byte n is data.charCodeAt(n)) string: UTF8编码的字符串; buffer: nodejs Buffer; array: Uint8Array,8位无符号数组; file: 文件的路径(仅nodejs下支持); 本例使用了 BinaryString 和 base64 两个格式解析

function fileToJson(file,callback) {
    // 数据处理结果
    let result;
    // 是否用BinaryString(字节字符串格式) 否则使用base64(二进制格式)
    let isBinary = true;
		// 创建一个FileReader对象
    var reader = new FileReader();
		// reader在onload解析结束事件时触发
    reader.onload = function (e) {
      var data = e.target.result;
      if (isBinary) {
        result = XLSX.read(data, {
          type: 'binary',
          cellDates: true,// 为了获取excel表格中的时间,返回格式为世界时间
        });
      } else {
        result = XLSX.read(btoa(fixdata(data)), {
          type: 'base64',
          cellDates: true,
        });
      }
      // 格式化数据
      formatResult(result,callback);
    };
    if (isBinary) {
      reader.readAsBinaryString(file);// 使用 BinaryString 来解析文件数据
    } else {
      reader.readAsArrayBuffer(file);// 使用base64 来解析文件数据
    }
  }

  // 文件流转 base64
  function fixdata(data) {
    var o = '',
      l = 0,
      w = 10240;
    for (; l < data.byteLength / w; ++l)
      o += String.fromCharCode.apply(
        null,
        new Uint8Array(data.slice(l * w, l * w + w))
      );
    o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)));
    return o;
  }

alt

上图就是用XLSX解析后的结果,由于解析成WorkBook的形式,不好读取数据,所以需要使用XLSX的方法XLSX.utils.sheet_to_json 该方法是将WorkBook中的sheet数据转成json,有两个参数:参数一是sheet数据,参数二是配置对象

alt

const sheetJson = XLSX.utils.sheet_to_json(sheets[item], { header: 1 });
// 配置header1表示生成二维数组,具体配置情况请看SheetJS官网配置

可是获取到的JSON数据没有考虑到合并单元格的情况(如图下),只给合并单元格的首个单元赋值,不是理想的结构格式,所以还需要再处理

alt

因为合并单元格的数据的相关位置XLSX也返回了,所以利用其处理数据结构。

alt

// 将读取的数据转成JSON
function formatResult(data,callback) {
    // 获取总数据
    const sheets = data.Sheets;
    // 获取每个表格
    const sheetItem = Object.keys(sheets);
  	// 返回sheetJSON数据源
    let sheetArr = [];
    // 获取
    sheetItem.forEach((item) => {
      const sheetJson = XLSX.utils.sheet_to_json(sheets[item], { header: 1 });
      console.log('sheetJson', sheetJson);
      // 格式化Item时间数据
      formatItemDate(sheetJson);
      // 格式化Item合并数据
      formatItemMerge(sheets[item], sheetJson);
       // 组合数据
      sheetArr.push({
        name: item,
        list: sheetJson
      })
    });
  	// 返回数据
  	callback(sheetArr)
  }

	// 格式化Item时间数据
  function formatItemDate(data) {
    data.forEach((row) => {
      row.forEach((item, index) => {
        // 若有数据为时间格式则格式化时间
        if (item instanceof Date) {
          // 坑:这里因为XLSX插件源码中获取的时间少了近43秒,所以在获取凌晨的时间上会相差一天的情况,这里手动将时间加上
          var date = new Date(Date.parse(item) + 43 * 1000);
          row[index] = `${date.getFullYear()}-${String(
            date.getMonth() + 1
          ).padStart(2, 0)}-${String(date.getDate()).padStart(2, 0)}`;
        }
      });
    });
    console.log('data', data);
  }

	 // 格式化Item合并数据
  function formatItemMerge(sheetItem, data) {
    // 保存每一项sheet中的合并单元格记录
    const merges = sheetItem['!merges'] || [];
    merges.forEach((el) => {
      const start = el.s;
      const end = el.e;
      // 处理行合并数据
      if (start.r === end.r) {
        const item = data[start.r][start.c];
        for (let index = start.c; index <= end.c; index++) {
          data[start.r][index] = item;
        }
      }
      // 处理列合并数据
      if (start.c === end.c) {
        const item = data[start.r][start.c];
        for (let index = start.r; index <= end.r; index++) {
          data[index][start.c] = item;
        }
      }
    });
  }

最后源码的地址: github.com/Cwolf-spiri…