在 Node.js 中利用 js-xlsx 處理 Excel 文件
來自: http://segmentfault.com/a/1190000004395728
簡介
本文介紹用 Node.js 中的 js-xlsx 庫來處理 Excel 文件。
js-xlsx 庫是目前 Github 上 star 數量最多的處理 Excel 的庫,功能強大,但上手難度稍大。文檔有些亂,不適合快速上手。
本文對 js-xlsx 庫進行一定的總結,并提供幾個實用的例子供讀者測試,學習,交流。
</div>
安裝
$ npm install xlsx
一些概念
在使用這個庫之前,先介紹庫中的一些概念。
-
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.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'] // 根據表名獲取對應某張表 const worksheet = workbook.Sheets[sheetNames[0]];
通過 worksheet[address] 來操作表格,以 ! 開頭的 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 } } ]</pre>
實戰
解析 Excel 生成 JSON
Tips事實上,你可以直接通過 XLSX.utils.sheet_to_json(worksheet) 獲得同樣的結果
注意本例子中假設表的第一行為字段名
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)); // 當前單元格的值 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 } ... ]</pre>
合并表格
步驟:
讀取多份表格
合并數組
Tips:其實合并表格跟 XLSX 沒什么關系,只是處理幾個數組而已。
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,再對統一處理 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: '???' } ]</pre>
導出表格
步驟:
構建特定的數據結構,如下。
調用 XLSX.writeFile(workbook, filename) 即可。
// workbook { SheetNames: ['mySheet'], Sheets: { 'mySheet': { '!ref': 'A1:E4', // 必須要有這個范圍才能輸出,否則導出的 excel 會是一個空表 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 添加對應的單元格位置 // [ { 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');</pre>
出處
http://scarletsky.github.io/2016/01/30/2016-01-30-nodejs-process-excel/
參考資料