用 JS 解析 excel 檔案需要分幾步

語言: CN / TW / HK

theme: cyanosis

持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第17天,點選檢視活動詳情

八月長江萬里晴,千帆一道帶風輕。

大家好,我是嘿嘿,今天來聊一聊如何使用 JS 來解析 excel 檔案,當然不是直接使用 exceljssheetjs 之類的庫,那就沒意思了,而是主要說一下 JS 解析 excel 表格是如何實現的。

注意本文主要討論 xlsx 格式的 excel 表格,其它格式未探究並不清楚。

excel 表格檔案到底是什麼

首先要解析 excel 檔案,得先了解他是如何儲存資料的,經過我百般搜尋,終於在 GG 中找到了答案:excel 檔案其實是一個 zip 包!於是我趕緊新建了一個 xlsx 檔案,在其中新建了兩個 sheet 表,兩個 sheet 表資料如下:

此為 sheet 1:

| A | B | C | | --- | --- | --- | | 1 | | 2 | | 1 | | 2 | | | | | | | | | | | | | | 1 | | 2 | | 1 | | 2 |

此為 sheet 2:

| A | B | | --- | --- | | q | a | | q | a | | q | a |

然後使用 zip 進行解壓:

sh unzip test.xlsx -d test

然後通過 tree 我們就拿到這樣一個目錄結構:

sh test ├── [Content_Types].xml ├── _rels ├── docProps │ ├── app.xml │ ├── core.xml │ └── custom.xml └── xl ├── _rels │ └── workbook.xml.rels ├── sharedStrings.xml ├── styles.xml ├── theme │ └── theme1.xml ├── workbook.xml └── worksheets ├── sheet1.xml └── sheet2.xml

啊哈,幹得漂亮,居然全都是 xml 檔案。

我們在開啟 xml 一探究竟,可以看出有幾個檔案很顯眼,就是 worksheets 下的 sheet1.xmlsheet2.xml,還有 workbook.xml,其他的 stylestheme 一看就是和樣式有關係,_rels 感覺就是什麼內部引用,我們先看看兩個 sheetxml 檔案,看看猜測是否正確,貼下 sheet1.xml

```xml

1 2 1 2 1 2 1 2 ```

😂 相信大家已經看出來了,sheetData 就是 excel 表格中的資料了,<row> 代表行,其中的 r 則是行數索引,row 中的 <c> 應該是 cell 了,其中的 <v> 對應著 cell 中的值,而 r 則是 cell 的位置,如 A7 代表著在 A 列 7 行。

此外還有幾個很明顯的屬性如 dimension 可以看出是表格的大小範圍,從 A1 cellC7 cell 形成一個框。<sheetViews> 中儲存的應該是頁面中的資訊,<selection> 代表的應該就是被選中的表格內容了。

workbook 中儲存的則是 sheet 的資訊:

```xml

```

剩下的幾個 xml,大概看了一眼,儲存的資訊還算很清楚,比如:

  • app 中儲存了檔案程式的資訊,好像還有檔名
  • core 中儲存了作者的資訊和建立、修改時間
  • rels 檔案也是 xml 格式,儲存了一些其它 xml 的引用
  • theme 裡儲存了表格中定義的顏色、字型
  • [Content_Types] 裡則是所有檔案的引用,猜測估計為解析的入口檔案

JS 實現步驟

知道了 excel 檔案是如何儲存資料的,那我們如何用 js 來解析它就很清楚了,主要分三步:

  1. 使用 js 解壓縮 excel 檔案
  2. 獲取到其中的 sheet 檔案內容,然後將 xml 資料解析出來
  3. 將資料轉換成我們想要的形狀

說幹就幹,那我們來實操一下:

ZIP 解壓

關於 JS 如何實現 ZIP 解壓的,上一篇文章也有提到,這裡我們就不細說,直接使用 jszip 搞定:

js document.querySelector('#file').addEventListener('change', async e => { const file = e.target.files[0]; if (!file) return; const zip = await JSZip.loadAsync(file); const sheetXML = await zip.files['xl/worksheets/sheet1.xml'].async('string'); });

快速搞定,現在 sheetXML 就是我們剛剛看到的 sheet1.xml 中的資料了。

XML 解析

然後我們即可解析 XML 內容將其中資料取出,xml 解析原理很簡單,和 html parse 一樣,瞭解原理咱就直接隨便搞個開源庫幫忙搞定:

```js import convert from 'xml-js';

const result = convert.xml2json(sheetXML, { compact: true, spaces: 4 }); ```

然後我們就得到了這樣一串 JSON(刪除了部分內容):

json { "_declaration": { "_attributes": {} }, "worksheet": { "_attributes": {}, "sheetPr": {}, "dimension": { "_attributes": { "ref": "A1:C7" } }, "sheetData": { "row": [ { "_attributes": { "r": "1", "spans": "1:3" }, "c": [ { "_attributes": { "r": "A1" }, "v": { "_text": "1" } }, { "_attributes": { "r": "C1" }, "v": { "_text": "2" } } ] }, { "_attributes": { "r": "7", "spans": "1:3" }, "c": [ { "_attributes": { "r": "A7" }, "v": { "_text": "1" } }, { "_attributes": { "r": "C7" }, "v": { "_text": "2" } } ] } ] } } }

接下來,我們只需要將 sheetData 中的資料取出,然後按照內部的屬性生成自己想要的資料格式即可。

總結

excel 檔案本質就是一個 zip 包,我們只需要通過 zip 解壓、xml 解析、資料處理這三個步驟,即可使用 JS 讀取到其中的資料,當然其中的細節還是很多的,不過如果只是簡單的 excel 模版,不妨自己嘗試一下。