XLSX-Renderer and Comlink

これの続きです。
備忘録です。


JSONデータをPHPで作成し(PHPじゃなくてもいいんだけど。。。)、クライアント・ブラウザ側のJavaScriptでエクセルファイルを作成するまではできました。
ただ、問題なのは、取得するJSONデータが結構大きいサイズ(例えば10万件とか)になると、それはそれで厄介です。

なんせ、JSONデータをfetchするだけでそれだけで時間がかかりますし、そのデータをエクセルファイルに差し込んでエクセルファイルを作成する時にも結構な時間がかかります。
その間、「データを取得中・・・エクセルファイルを生成中・・・」とかでUIを止めてもいいのですが・・・それはイマドキのトレンドではありませんよね。

せっかくブラウザがワーカースレッドを使えるようにしてくれているので、別スレッドにそういう処理は任してしまいましょう。
そう! 我々にはグーグル様が書いてくれたComlink がある!(笑)

今回は、サンプルとして↓のように、郵便番号のデータベース(SQLite)から、リクエストに応じてJSONデータを返すPHPで書いたウェブAPIを使用し、そのデータをエクセルファイルに変換するまでを書いてみる。


まずは、ウェブAPIですが・・・これはサクッと書いてしまいましょう。
パラメータ”q”に郵便番号の先頭数文字を指定して前方一致で検索してJSONとしてレスポンスを返します。
郵便番号辞書の元になるデータは日本郵便サイトからCSVファイルが公開されているのでそれを使用しSQLiteに変換して使用しました。

次に、エクセルファイルを作成する部分のJavaScriptコードです。
以前にも書きましたが、この部分はnode.jsとnpm, babel.jsとかが必要です。この部分が全く分からなければこの先見てもワケワカンネー状態になるので、とっととブラウザを閉じてこのサイトの事は忘れてくださいm(__)m

作業ディレクトリを一個作って、以下のpackage.jsonとrenderer.jsを作成。

>> tree .
.
├── package.json
└── renderer.js

※ 2022/02/06 rendererをDedicatedWorkerGlobalScopeに追加するようにrenderer.js を若干修正しました。

で、下記を実行して renderer.pack.min.js が生成されるので、このファイルを workerスレッド内からimportScripts関数でロードします。

>> npm i
>> npm run bundle

これで準備完了です。
ここからは、実際に画面(HTML)とメインスレッドのjs、ワーカースレッドのjsを書いていきます。
例によって Web Workerを利用する際には、Comlink を使ってラクします。

HTML等はデモ画面を参照。

まずは、取得したJSONデータをExcelに差し込むための、テンプレートとなるエクセルファイルを作成します。
フツーにエクセルで作成していきます。Microsoft Officeを持っていない場合は、LibreとかオープンソースのOfficeアプリとかでも構いません。
.xlsx形式のファイルを出力できる表計算アプリであれば何でもいいと思います。

こんな感じです。テンプレート XLSXファイル

これらの書式等は、XLSX-Rendererのドキュメント参照。

次にメインスレッド側のJavaScriptコードです。
特筆すべき点はありません。見ればだいたい分かるでせう。
ワーカースレッドのやり取りに Comlink を使ってます。

このファイルをロードする際は、type属性を text/javascript 等ではなく、module にしないとなんかエラーになるみたいです。
そしてボタンのクリックハンドラを設定します。(ここだけjQueryを使用しています)

最後はワーカースレッド側のJavaScriptコードです。
Comlinkを使って、メインスレッド側に関数を公開しています。

※ 2022/02/06 若干修正

このファイル内で、上記で準備した renderer.pack.min.js をロードします。


上記デモページ


たぶんこの程度の処理なら、わざわざワーカースレッドを起こさなくても、いいかとも思います。実際問題、複雑になるだけですし。
メインスレッドはUI専用にして、雑多な処理を全部 workerスレッドに投げるようにすれば、ちょっとハッピーになれる・・・(かもしれない)。

SharedWorkerとかも興味があるんだけど、スマートフォンであまり対応していないんだよね。やっぱバッテリーの電気を食うからなんだろうか・・・。

JSONデータをExcelファイルに展開したい、ブラウザで。

2022年3月8日 一部記述削除


2021年7月31日 修正
babelの設定を書き忘れてた!package.jsonにbabelの設定を追加。


これの続編

サーバーで処理させるよりデータだけをWeb APIで引っ張ってきてブラウザ上のJavaScriptで処理しよう、っていうのがここ最近のトレンドだと思います。そのための環境は整ってきました。

ここ最近、業務系のウェブシステムでExcelをあーだこーだしろ、っていうのが急に増えてきてまして・・・たぶん昔動かしていたAccessアプリケーションを単純にWebへ・・・という流れなんでしょうかね?

で・・・Webサーバー(LAMP)でPHP-Excel(PHP-Spreadsheet)使ってエクセルファイルを処理させると滅茶苦茶メモリ食いませんか?
数年前に少し試してみたんですが、通常業務で利用するぐらいの行数(列数)でもメモリ不足でエラーでるわ、処理が遅くて使いもんにならんわ、で使っていくのを速攻で止めました。
(PHPじゃなくてJavaとかだと速いのかもしれんが、Javaは難易度が高すぎるし、そもそもLAMPを主戦場にしている仕事環境なので。)

さて、ちょっと前に Excelファイルを読み書きする SheetJS っていうのをネタに備忘録として書いたけど、SheetJSは Pro版じゃないと(有料)、スタイルなど文字の大きさとかフォントの指定だとかは扱えません。実質、既存のExcelのデータを読むだけになると思います。まぁPro版買えって話なんですけど。

で、良い感じにブラウザでカンタンにきれいなExcel帳票を作れないかなぁ・・・といろいろ githubとか漁ってたら、XLSX-Rendererっていうのを見つけました。簡単に言うと、WORDでいうところの差し込み印刷のように、テンプレートのExcelファイルにデータを差し込んでExcelファイルを作ってくれる。
XLSX-Renderer自体は ExcelJS というnodeモジュールを使用しているみたい。

XLSX-Renderer
https://github.com/Siemienik/XToolset/tree/master/packages/xlsx-renderer

まぁ、いつもの如く中身はよく分かんねーけど一度試してみました。今回もその備忘録です。
【動作デモはこちらから】

開発環境としてnodejsとnpmが使える環境が大前提です。
また、モダンなJavaScriptの知識( Promise,await/async,thenなど)と、必要であれば、BabelJSなどのカンタンな使い方を知っている人向けです。じゃないとワケワカメになると思います。ご了承のほど。

システムにbrowserify,uglify-jsをインストール

>> sudo npm install -g browserify
>> sudo npm install -g uglify-js

説明するまでもないとは思いますが、browserifyはnodejsモジュールをブラウザで利用できるようにワンパッケージにしてくれるコマンド。uglifyjsはminifyするため。詳しくは知らない。手順だけ知っておけばいい。理屈は後回し。フロントエンド周りに詳しい人は webpack とかいろいろ方法はあるとは思いますが、僕はこれしか知らないので💦

まず、下記のようなテンプレートとなるExcelファイルを作ります。(テンプレートExcelファイル

このExcelファイルに、データをぶち込みたいところへ、#から始まる命令コマンドをセルに埋め込んでいく、という感じ。自分でセルを埋め込むメソッドをチマチマコーディングするより圧倒的にラクできる感じ。
#! FOR_EACH 命令を使えば、配列をぶち込める。

要は MVCモデルの View の出力先を エクセルファイルにしている感じでしょうか。同様に、DOCX-Renderer とか PDF-Renderer ってのも作ってほしいよ(笑)

次に本題の処理する部分をコーディングします。

扱いやすいように、僕はグローバル関数を(windowオブジェクトのプロパティとして)定義してコールするようしました。このあたりはどういう処理を行わせるかによって実装方法は変わると思います。とりあえず関数として実装しました。
このままではブラウザでは動かないので、上記のコードをブラウザで利用できるようにするため、この xlsx-template.jsをbabelなどのツールを使ってビルドします。そのため、ディレクトリを一個作って以下のpackage.jsonを置き、

>> tree .
.
├── package.json
└── xlsx-template.js

そのディレクトリで以下を実行

>> npm install
>> npm run bundle

そうすると、bundle.min.jsが生成されるので、これをHTMLファイルのscriptタグからロードします。
HTMLファイルはこんな感じでしょうか。実際にはJSONデータはサーバーから取得することになりますが・・・。データ構造は簡単で、キーと値がそのままテンプレートのExcelファイルのセルに記述した “## キー名” に対応してます。このあたりは XLSX-Renderer のgithubページに記述の仕方が書かれているので参考に。比較的簡単です。

そうすると、下記のようにテンプレートとなるExcelファイルにデータを埋めてくれます。

【動作デモ】

エクセルのレイアウトが変わっても、テンプレートのExcelファイルを変更すればいいだけので、変更も簡単で、実行コードを修正する必要もない。

まだ試していませんが・・・データベースサーバーからウェブサーバー経由で1万件ぐらいのJSONデータを取ってきて、処理させてテストしてみて、処理速度が業務に耐えられるようであれば、本格的に利用していきたい、と思ってます。

・・・しかし・・・みんなExcel好きだよねぇ。。。