Node.js利用js-xlsx處理Excel文件的方法詳解
簡(jiǎn)介
本文介紹用 Node.js 中的 js-xlsx 庫來處理 Excel 文件。
js-xlsx 庫是目前 Github 上 star 數(shù)量最多的處理 Excel 的庫,功能強(qiáng)大,但上手難度稍大。文檔有些亂,不適合快速上手。
本文對(duì) js-xlsx 庫進(jìn)行一定的總結(jié),并提供幾個(gè)實(shí)用的例子供讀者測(cè)試,學(xué)習(xí),交流。
安裝
$ npm install xlsx
一些概念
在使用這個(gè)庫之前,先介紹庫中的一些概念。
- workbook 對(duì)象,指的是整份 Excel 文檔。我們?cè)谑褂?js-xlsx 讀取 Excel 文檔之后就會(huì)獲得 workbook 對(duì)象。
- worksheet 對(duì)象,指的是 Excel 文檔中的表。我們知道一份 Excel 文檔中可以包含很多張表,而每張表對(duì)應(yīng)的就是 worksheet 對(duì)象。
- cell 對(duì)象,指的就是 worksheet 中的單元格,一個(gè)單元格就是一個(gè) cell 對(duì)象。
它們的關(guān)系如下:
// workbook
{
SheetNames: ['sheet1', 'sheet2'],
Sheets: {
// worksheet
'sheet1': {
// cell
'A1': { ... },
// cell
'A2': { ... },
...
},
// worksheet
'sheet2': {
// cell
'A1': { ... },
// cell
'A2': { ... },
...
}
}
}
用法
基本用法
- 用
XLSX.readFile打開 Excel 文件,返回 workbook - 用
workbook.SheetNames獲取表名 - 用
workbook.Sheets[xxx]通過表名獲取表格 - 按自己的需求去處理表格
- 生成新的 Excel 文件
具體用法
讀取 Excel 文件
import XLSX from 'xlsx';
const workbook = XLSX.readFile('someExcel.xlsx', opts);
獲取 Excel 文件中的表
// 獲取 Excel 中所有表名 const sheetNames = workbook.SheetNames; // 返回 ['sheet1', 'sheet2'] // 根據(jù)表名獲取對(duì)應(yīng)某張表 const worksheet = workbook.Sheets[sheetNames[0]];
通過 worksheet[address] 來操作表格,以 ! 開頭的 key 是特殊的字段。
// 獲取 A1 單元格對(duì)象
let a1 = worksheet['A1']; // 返回 { v: 'hello', t: 's', ... }
// 獲取 A1 中的值
a1.v // 返回 'hello'
// 獲取表的有效范圍
worksheet['!ref'] // 返回 'A1:B20'
worksheet['!range'] // 返回 range 對(duì)象,{ s: { r: 0, c: 0}, e: { r: 100, c: 2 } }
// 獲取合并過的單元格
worksheet['!merges'] // 返回一個(gè)包含 range 對(duì)象的列表,[ {s: { r: 0, c: 0 }, c: { r: 2, c: 1 } } ]
實(shí)戰(zhàn)
解析 Excel 生成 JSON
Tips 事實(shí)上,你可以直接通過 XLSX.utils.sheet_to_json(worksheet) 獲得同樣的結(jié)果
注意 本例子中假設(shè)表的第一行為字段名
const headers = {};
const data = [];
const keys = Object.keys(worksheet);
keys
// 過濾以 ! 開頭的 key
.filter(k => k[0] !== '!')
// 遍歷所有單元格
.forEach(k => {
// 如 A11 中的 A
let col = k.substring(0, 1);
// 如 A11 中的 11
let row = parseInt(k.substring(1));
// 當(dāng)前單元格的值
let value = worksheet[k].v;
// 保存字段名
if (row === 1) {
headers[col] = value;
return;
}
// 解析成 JSON
if (!data[row]) {
data[row] = {};
}
data[row][headers[col]] = value;
});
console.log(data); // [ { '姓名': 'test1', '年齡': 20 }, { '姓名': 'test2', '年齡': 10 } ... ]
合并表格
步驟:
- 讀取多份表格
- 合并數(shù)組
Tips: 其實(shí)合并表格跟 XLSX 沒什么關(guān)系,只是處理幾個(gè)數(shù)組而已。
sheet1
| id | name | age |
|---|---|---|
| 1 | test1 | 30 |
| 2 | test2 | 20 |
| 3 | test3 | 18 |
sheet2
| id | country | remark |
|---|---|---|
| 1 | China | hello |
| 2 | America | world |
| 3 | Unkonw | ??? |
let sheet1 = XLSX.utils.sheet_to_json(sheet1);
let sheet2 = XLSX.utils.sheet_to_json(sheet2);
// 先合并 sheet1 和 sheet2,再對(duì)統(tǒng)一處理
const result = sheet1.concat(sheet2).reduce((prev, next) => {
let index = prev.findIndex((elem, i) => elem.id === next.id);
if (index === -1) {
return prev.concat(next);
} else {
prev[index] = Object.assign({}, prev[index], next);
return prev;
}
}, []);
console.log(result);
// [ { 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: '???' } ]
導(dǎo)出表格
步驟:
- 構(gòu)建特定的數(shù)據(jù)結(jié)構(gòu),如下。
- 調(diào)用
XLSX.writeFile(workbook, filename)即可。
// workbook
{
SheetNames: ['mySheet'],
Sheets: {
'mySheet': {
'!ref': 'A1:E4', // 必須要有這個(gè)范圍才能輸出,否則導(dǎo)出的 excel 會(huì)是一個(gè)空表
A1: { v: 'id' },
...
}
}
}
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 添加對(duì)應(yīng)的單元格位置
// [ { 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 }))
// 轉(zhuǎn)換成 worksheet 需要的結(jié)構(gòu)
// { 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 的位置,生成對(duì)應(yīng)的單元格數(shù)據(jù)
// [ [ { 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) })))
// 對(duì)剛才的結(jié)果進(jìn)行降維處理(二維數(shù)組變成一維數(shù)組)
// [ { 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))
// 轉(zhuǎn)換成 worksheet 需要的結(jié)構(gòu)
// { 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);
// 計(jì)算出范圍
var ref = outputPos[0] + ':' + outputPos[outputPos.length - 1];
// 構(gòu)建 workbook 對(duì)象
var wb = {
SheetNames: ['mySheet'],
Sheets: {
'mySheet': Object.assign({}, output, { '!ref': ref })
}
};
// 導(dǎo)出 Excel
XLSX.writeFile(wb, 'output.xlsx');
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
出處
http://scarletsky.github.io/2016/01/30/nodejs-process-excel/
參考資料
https://github.com/SheetJS/js-xlsx
http://stackoverflow.com/questions/30859901/parse-xlsx-with-node-and-create-json
相關(guān)文章
Node.js對(duì)MongoDB數(shù)據(jù)庫實(shí)現(xiàn)模糊查詢的方法
模糊查詢是數(shù)據(jù)庫的基本操作之一,下面這篇文章主要給大家介紹了利用Node.js對(duì)MongoDB數(shù)據(jù)庫實(shí)現(xiàn)模糊查詢的方法教程,文中給出了詳細(xì)的介紹和示例代碼,對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。2017-05-05
nvm報(bào)錯(cuò)Now?using?node?v版本號(hào)(64-bit)圖文解決方法
這篇文章主要給大家介紹了關(guān)于nvm報(bào)錯(cuò)Now?using?node?v版本號(hào)(64-bit)的解決方法,文中將解決的辦法介紹的非常詳細(xì),對(duì)遇到這個(gè)問題的朋友具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-11-11
Node.js?中常用內(nèi)置模塊(path?路徑模塊)
這篇文章主要介紹了Node.js?中常用內(nèi)置模塊(path?路徑模塊),文章圍繞主題展開詳細(xì)的相關(guān)介紹,具有一定的參考價(jià)值,感興趣的朋友可以參考一下2022-09-09
一會(huì)帶你學(xué)會(huì)用Webpack搭建開發(fā)環(huán)境并打包代碼
這篇文章主要給大家介紹了關(guān)于如何用Webpack搭建開發(fā)環(huán)境并打包的相關(guān)資料,webpack是一個(gè)現(xiàn)代JavaScript應(yīng)用程序的靜態(tài)模塊打包器(module bundler),需要的朋友可以參考下2023-08-08
node.js?express和koa中間件機(jī)制和錯(cuò)誤處理機(jī)制
這篇文章主要介紹了node.js?express和koa中間件機(jī)制和錯(cuò)誤處理機(jī)制,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07
從Node.js事件觸發(fā)器到Vue自定義事件的深入講解
這篇文章主要給大家介紹了關(guān)于從Node.js事件觸發(fā)器到Vue自定義事件的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06

