タイトルの通り、JavaScriptでExcelファイルを取り扱う方法の紹介記事です。Excelファイルを取り扱うために使うパッケージはSheetJSです。これはJavaScript上でExcelファイルを操作するためのコード群で、2012年から発展し続けているかなり老舗のパッケージです。npmに公開されているので例によって npm install や yarn addでプロジェクトにインストールします。
SheetJS/sheetjs: SheetJS Community Edition — Spreadsheet Data Toolkitxlsx – npm
とりあえずweb上からマクロ付きExcelシート取得して加工してダウンロードするサンプルが次です。
// ExcelRepository.ts
// ブラウザとサーバとクライアントの仲立ちコード
import XLSX from "xlsx"; // SheetJSのパッケージ
import axios from "axios";
const root = "/assets/excel";
export class ExcelRepository {
/** Excelファイルを取得する関数 */
static async getFile(filename: string) {
// axiosでweb上からExcelファイルを取得。responseType;arraybuffer等のデータを破壊しない形式の必要あり
const excelFileResponse = await axios.get([root, filename].join("/"), {
responseType: "arraybuffer"
});
// バイナリの値を格納した配列としてSheerJSに渡す
return XLSX.read(new Uint8Array(excelFileResponse.data), {
type: "array",
bookVBA: true.// VBA(マクロ)ありを扱うと示すフラグオプション
});
}
/** Excelファイルをブラウザからユーザの手元にダウンロードさせる関数 */
static downloadToClient(wb:WorkBook,filename: string) {
return XLSX.writeFile(wb, filename, {
bookVBA: true,
})
}
}
// index.ts
// 処理本体
(async () => {
try {
// Excelファイルオブジェクト(以下WorkBook)を取得
const report = await ExcelRepository.getFile("withMacro.xlsm");
report.SheetNames.forEach(name=>{
// SheetNamesプロパティにはその名の通り各シートの名前が入っている。これを元にシートを探すコードを書けばNull参照を起こしにくい
report.Sheets[name]['B2'].v = Math.random() // セルB2にランダムな数字を代入
})
// Excelファイルをクライアント(ブラウザ外)にダウンロード
ExcelRepository.downloadToClient(report, 'withMacro2.xlsm');
} catch (e) {
console.error(e)
}
})();
環境に応じたデモ用コードが大量に用意されているためGitHubからクローンしてプロジェクト内の/demos以下を見ると使い方の参考としてはかどります。文字列、バイナリ、配列などの様々なインプット、アウトプットの型のみならずreact, vue.js, angularの様な様々なフレームワークにおける使い方例までサポートしてあります。
とんでもなく多様なパターンに対応しているためソースコード内の検索(コード自体を読むのではなく定義を探す)が重要です。demosから外れて細かいことをする場合、型ファイル/node_modules/xlsx/types/index.d.tsとコメントを見ながら追うのがいいです。例えば、セルは次の構造体で定義されています。
// /node_modules/xlsx/types/index.d.ts
/** Worksheet Cell Object */
export interface CellObject {
/** The raw value of the cell. Can be omitted if a formula is specified */
v?: string | number | boolean | Date;
/** Formatted text (if applicable) */
w?: string;
/**
* The Excel Data Type of the cell.
* b Boolean, n Number, e Error, s String, d Date, z Empty
*/
t: ExcelDataType;
/** Cell formula (if applicable) */
f?: string;
/** Range of enclosing array if formula is array formula (if applicable) */
F?: string;
/** Rich text encoding (if applicable) */
r?: any;
/** HTML rendering of the rich text (if applicable) */
h?: string;
/** Comments associated with the cell */
c?: Comments;
/** Number format string associated with the cell (if requested) */
z?: NumberFormat;
/** Cell hyperlink object (.Target holds link, .tooltip is tooltip) */
l?: Hyperlink;
/** The style/theme of the cell (if applicable) */
s?: any;
}
このようなundefinedもありうる簡素な名前のプロパティや関数を持つ構造体を多く持つ多重構造のデータを取り扱っています。どこを弄ればどうなるか初見で想像するには辛い命名が少なくないのでIDEの機能を生かして定義へジャンプ、その場でプロパティ等に紐づいたコメントを読むなどして扱いやすくするように工夫した方が断然お得です。