node操作excel

node操作excel
 最后更新于 2024年10月02日 22:59:18

概念

  • workbook 对象,整个Excel文档。使用js-xlsx读取Excel文档时返回workbook对象。
  • worksheet 对象,Excel文档中的表。一份Excel文档中可以包含很多张表,而每张表对应的就是worksheet对象。
  • cell 对象,worksheet中的单元格,一个单元格就是一个cell对象。

示例:

// workbook
{
    SheetNames: ['sheet1', 'sheet2'],
    Sheets: {
        // worksheet
        'sheet1': {
            // cell
            'A1': { ... },
            // cell
            'A2': { ... },
            ...
        },
        // worksheet
        'sheet2': {
            // cell
            'A1': { ... },
            // cell
            'A2': { ... },
            ...
        }
    }
}

用法

基本用法

  • XLSX.read读取Excel,返回workbook
  • XLSX.readFile打开Excel文件,返回workbook
  • workbook.SheetNames 获取表名
  • workbook.Sheets[xxx]通过表名获取表格
  • worksheet[address]操作单元格
  • XLSX.utils.sheet_to_json单个表的数据转换为json格式
  • XLSX.writeFile(wb, 'output.xlsx') 生成新的 Excel 文件

具体用法

1、读取 Excel 文件
workbook = XLSX.read(excelData, {type: 'base64'});
workbook = XLSX.writeFile('someExcel.xlsx', opts);
2、获取 Excel 文件中的表
// 获取 Excel 中所有表名
var sheetNames = workbook.SheetNames; // 返回 ['sheet1', 'sheet2',……]
// 根据表名获取对应某张表
var worksheet = workbook.Sheets[sheetNames[0]];
3、通过 worksheetaddress 来操作表格,以 ! 开头的 key 是特殊的字段。
// 获取 A1 单元格对象
let a1 = worksheet['A1']; // 返回 { v: 'hello', t: 's', ... }
// 获取 A1 中的值
a1.v // 返回 'hello'

// 获取表的有效范围
worksheet['!ref'] // 返回 'A1:B20'
worksheet['!range'] // 返回 range 对象,{ s: { r: 0, c: 0}, e: { r: 100, c: 2 } }

// 获取合并过的单元格
worksheet['!merges'] // 返回一个包含 range 对象的列表,[ {s: { r: 0, c: 0 }, c: { r: 2, c: 1 } } ]
4、获取 Excel 文件中的表转换为JSON数据
XLSX.utils.sheet_to_json(worksheet)  //针对单个表,返回序列化json数据
5、生成新的 Excel 文件
// 服务端通过XLSX.writeFile
XLSX = require("xlsx");
XLSX.writeFile(wb, 'output.xlsx')

// 客服端,只能通过XLSX.write(wb, write_opts) 写入 表格数据,借助FileSaver生成,且只支持在高版本浏览器。
var wopts = { bookType:'xlsx', bookSST:false, type:'binary' };

var wbout = XLSX.write(wb,wopts);

function s2ab(s) {
  var buf = new ArrayBuffer(s.length);
  var view = new Uint8Array(buf);
  for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
  return buf;
}

/* the saveAs call downloads a file on the local machine */
saveAs(new Blob([s2ab(wbout)],{type:""}), "test.xlsx")

js-xlsx实战

1、解析 Excel 生成 JSON
function to_json(workbook) {
  var result = {};

  // 获取 Excel 中所有表名
  var sheetNames = workbook.SheetNames; // 返回 ['sheet1', 'sheet2']

  workbook.SheetNames.forEach(function(sheetName) {
    var worksheet = workbook.Sheets[sheetName];
    result[sheetName] = XLSX.utils.sheet_to_json(worksheet);
  });

  console.log("打印表信息",JSON.stringify(result, 2, 2));  // 显示格式{"表1":[],"表2":[]}
  return result;
}
2、导出表格
  1. 构建特定的数据结构,通过new Blob如下。
// workbook
{
    SheetNames: ['mySheet'],
    Sheets: {
        'mySheet': {
            '!ref': 'A1:E4', // 必须要有这个范围才能输出,否则导出的 excel 会是一个空表
            A1: { v: 'id' },
            ...
        }
    }
}
  1. 调用 XLSX.write, 借助FileSaver中new Blob生成即可。
var _headers = ['id', 'name', 'age', 'country', 'remark']
var _data = [ { id: '1',
                name: 'test1',
                age: '30',
                country: 'China',
                remark: 'hello' },
              { id: '2',
                name: 'test2',
                age: '20',
                country: 'America',
                remark: 'world' },
              { id: '3',
                name: 'test3',
                age: '18',
                country: 'Unkonw',
                remark: '???' } ];

var headers = _headers
                // 为 _headers 添加对应的单元格位置
                // [ { v: 'id', position: 'A1' },
                //   { v: 'name', position: 'B1' },
                //   { v: 'age', position: 'C1' },
                //   { v: 'country', position: 'D1' },
                //   { v: 'remark', position: 'E1' } ]
                .map((v, i) => Object.assign({}, {v: v, position: String.fromCharCode(65+i) + 1 }))
                // 转换成 worksheet 需要的结构
                // { A1: { v: 'id' },
                //   B1: { v: 'name' },
                //   C1: { v: 'age' },
                //   D1: { v: 'country' },
                //   E1: { v: 'remark' } }
                .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v}}), {});

var data = _data
              // 匹配 headers 的位置,生成对应的单元格数据
              // [ [ { v: '1', position: 'A2' },
              //     { v: 'test1', position: 'B2' },
              //     { v: '30', position: 'C2' },
              //     { v: 'China', position: 'D2' },
              //     { v: 'hello', position: 'E2' } ],
              //   [ { v: '2', position: 'A3' },
              //     { v: 'test2', position: 'B3' },
              //     { v: '20', position: 'C3' },
              //     { v: 'America', position: 'D3' },
              //     { v: 'world', position: 'E3' } ],
              //   [ { v: '3', position: 'A4' },
              //     { v: 'test3', position: 'B4' },
              //     { v: '18', position: 'C4' },
              //     { v: 'Unkonw', position: 'D4' },
              //     { v: '???', position: 'E4' } ] ]
              .map((v, i) => _headers.map((k, j) => Object.assign({}, { v: v[k], position: String.fromCharCode(65+j) + (i+2) })))
              // 对刚才的结果进行降维处理(二维数组变成一维数组)
              // [ { v: '1', position: 'A2' },
              //   { v: 'test1', position: 'B2' },
              //   { v: '30', position: 'C2' },
              //   { v: 'China', position: 'D2' },
              //   { v: 'hello', position: 'E2' },
              //   { v: '2', position: 'A3' },
              //   { v: 'test2', position: 'B3' },
              //   { v: '20', position: 'C3' },
              //   { v: 'America', position: 'D3' },
              //   { v: 'world', position: 'E3' },
              //   { v: '3', position: 'A4' },
              //   { v: 'test3', position: 'B4' },
              //   { v: '18', position: 'C4' },
              //   { v: 'Unkonw', position: 'D4' },
              //   { v: '???', position: 'E4' } ]
              .reduce((prev, next) => prev.concat(next))
              // 转换成 worksheet 需要的结构
              //   { A2: { v: '1' },
              //     B2: { v: 'test1' },
              //     C2: { v: '30' },
              //     D2: { v: 'China' },
              //     E2: { v: 'hello' },
              //     A3: { v: '2' },
              //     B3: { v: 'test2' },
              //     C3: { v: '20' },
              //     D3: { v: 'America' },
              //     E3: { v: 'world' },
              //     A4: { v: '3' },
              //     B4: { v: 'test3' },
              //     C4: { v: '18' },
              //     D4: { v: 'Unkonw' },
              //     E4: { v: '???' } }
              .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v}}), {});

// 合并 headers 和 data
var output = Object.assign({}, headers, data);
// 获取所有单元格的位置
var outputPos = Object.keys(output);
// 计算出范围
var ref = outputPos[0] + ':' + outputPos[outputPos.length - 1];

// 构建 workbook 对象
var wb = {
    SheetNames: ['mySheet'],
    Sheets: {
        'mySheet': Object.assign({}, output, { '!ref': ref })
    }
};

// 导出 Excel
//XLSX.writeFile(wb, 'output.xlsx');

var wopts = { bookType:'xlsx', bookSST:false, type:'binary' };

var wbout = XLSX.write(wb,wopts);

function s2ab(s) {
  var buf = new ArrayBuffer(s.length);
  var view = new Uint8Array(buf);
  for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
  return buf;
}

/* the saveAs call downloads a file on the local machine */
saveAs(new Blob([s2ab(wbout)],{type:""}), "test.xlsx")