最近写了一个脚本使用JS读取Excel的数据后进行随机组合排列后再写入Excel文件。使用到了node-xlsx 库用于操作Excel文件。为什么没有使用大名鼎鼎的js-xlsx,是因为发现node-xlsx其实是依赖于js-xlsx的,它对js-xlsx接口进行了封装对使用者更友好些,特别是读取Excel文件解析后的数据格式更友好些。

读取xlsx文件

1
2
3
4
5
6
7
#!/usr/local/bin node
const xlsx = require("node-xlsx");
const fs = require('fs');
const path = require('path');

const XLSX = path.join(__dirname, '随机名单.xlsx'); // 获取xlsx文件路径
let xlsxData = xlsx.parse(fs.readFileSync(XLSX)); // 解析xlsx文件数据

读取后的数据是个数组类型,每个数组项代表Excel中的一张表。name是表名,data是表数据。

如上图,data数据格式是个数组,每个数组项式是表中的一行数据。

如上图,如果有单元格合并,解析的数据会留空位置(undefined)对应表格中的被合并的单元格。

写xlsx文件

1
2
3
4
5
6
7
8
9
10
const xlsx = require("node-xlsx");
const fs = require('fs');
const path = require('path');

const data = [[1, 2, 3], [true, false, null, 'sheetjs'], ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'], ['baz', null, 'qux']];
const range = {s: {c: 0, r:0 }, e: {c:0, r:3}}; // 合并单元格
const option = {'!merges': [ range ]}; // 配置写入项 !merges表示合并单元格

var buffer = xlsx.build([{name: "mySheetName", data: data}], option); // Returns a buffer
fs.writeFileSync(path.join(__dirname,'./test.xlsx'), buffer); // 写入数据

js-xlsx中,一个单元格的对象的位置有{c:C, r:R}表示,c表示列(column)的位置,r表示行(row)位置。

A1的单元格坐标是{c:0,r:0}

{s:{c:C, r: R}, e: {c:C1, r:R1}}表示单元格区间,s表示起始单元格,e表示最后一个单元格。

{s: {c: 0, r: 0}, e: {c: 0, r: 3}}表示A1:A4单元格区间。

发送excel文件

发送excel文件到浏览器端的关键是设置好 header 的 Content-Type 值。

对于excel,Content-Type有如下两种:

1
2
3
4
5
// .xls 文件
application/vnd.ms-excel

// .xlsx 文件
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

下面以restify为例写一段demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
et restify = require('restify'),
xlsx = require('node-xlsx');

let app = restify.createServer({
name: 'demo-xlsx',
version: '1.0.0'
});

// 访问 http://127.0.0.1:8001/test,即下载myfile.xlsx文件
app.get('/test', function(req, res, next) {
const data = [[1, 2, 3], [true, false, null, 'sheetjs'], ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'], ['baz', null, 'qux']];
return sendExcel(res, data, 'sheet表名', 'myfile');
});

app.listen(8001, function() {
console.log(app.name, 'Start listening at %s', app.url);
});

/**
* 写入Excel
* @param res Response对象
* @param xlsxData xlsx数组
* @param sheetName excel表名
* @param xlsxFileName excel文件名(备注:不要使用中文)
*/
function sendExcel(res, xlsxData, sheetName, xlsxFileName) {
try {
let buffer = xlsx.build([{name: sheetName, data: xlsxData}]);
let xlsxContentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; // For Excel2007 and above .xlsx files

res.setHeader('Content-Type', xlsxContentType);
res.setHeader('Content-Disposition', `attachment; filename=${xlsxFileName}.xlsx`);
res.writeHead(200);
res.end(buffer);
} catch (err) {
console.log('mistake to build excel');
}
}