Blazor WebAssembly 製マークシートシステム Mark2 のダウンロードサイズ 100 MB を、20 MB に削減する

## Blazor WebAssembly 製マークシートシステム Mark2 はオープンソースで公開されています

普通紙に記入されたマークシートの画像を読み取ってデータ化する Blazor WebAssembly 製のクライアント Web アプリケーション「Mark2」が、オープンソースとして公開されたとの記事が先日公開されました。

https://qiita.com/kaorumori/items/5b7255e3244159b082d3

オフライン対応された PWA として作られているようですので、いちどアクセスしてインストールするなりホーム画面やブックマークに追加するなりしておけば、インターネット接続がなくても動作するようです。このような Web アプリケーションが MIT license のオープンソースとして公開されているのは素晴らしいですね!

## ただしダウンロードサイズが 100 MB超

さてその Mark2 ですが、公開サイト https://mark2.defrag.works/ にアクセスしてみたところ、自分の試した限りでは、その Mark2 の**ダウンロードサイズが 101 MB ほど**になりました。
![Brotli Loader を入れる前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/b4df0921-7cb3-f0b8-7d31-932ae4a568f0.png)
クライアント Web アプリケーションのダウンロードサイズとしては、ちょっと大きく感じるかもしれませんね。もともと Blazor WebAssembly は、他の JavaScript 製 クライアント Web アプリケーションフレームワークと比べると、そのコンテンツダウンロードサイズが膨らみがちという点がありました。加えて、Mark2 では処理速度を稼ぐために、予め WebAssembly ネイティブにビルドする AOT という技法が使われていますが、この AOT を有効にするとさらにコンテンツサイズが膨らみます。それで結果として 100 MB 超というダウンロードサイズに至ったようです。

とはいえ、Mark2 もその前身は WPF による Windows 用の GUI アプリケーションだったということですし、ちょっとした規模のその手のデスクトップアプリケーションをインストールするのにダウンロードするときには、ダウンロードサイズが 100 MB 前後でもあまり気に留めてなかったようにも思います。ですので、Mark2 もデスクトップアプリケーションのインストールと比べて落ち着いて考えると、まぁ、100 MB くらいあってもそんなものだよねぇ、とも思えます。

念のためもう一点補足しておきますと、**Blazor WebAssembly アプリケーションはそのコンテンツをブラウザのキャッシュストレージに保存**します。なので、Mark2 の利用にあたっても、たしかに初回は 100 MB ほどのダウンロードは必要ですが、**2回目以降のアクセスでは多くても数 10 KB 程度**しかダウンロードが発生しません。

## HTTP 通信上で圧縮効かせれば改善されるよ!

幸い、近代の HTTP 通信においてはコンテンツを圧縮してダウンロードできるオプションがあります。圧縮方式にはいくつかあるようですが、近年では **Brotli 方式** (詳細は下記リンク先など参照) が圧縮率も高くよく使われつつあるように感じています。

https://ja.wikipedia.org/wiki/Brotli

なので、コンテンツを Brotli 圧縮してブラウザに返信する Web サーバー上に Mark2 を設置すれば、それだけで随分とダウンロードにかかる通信サイズが縮小できるはずです。ただし私が見た限りでは、Mark2 のコンテンツは Amazon S3 上に配置されているようで、および、**Amazon S3 からのコンテンツ配信では、Brotli 圧縮はもちろん、GZip 圧縮もされずに素の状態のまま送られてくる**ようです。実のところ、コンテンツを圧縮してブラウザに返送するには、その圧縮処理がサーバーにはかなりの負担になります。ですから、Amazon S3 からの配信でコンテンツの自動圧縮に期待するほうが筋違いなのでしょう。

Blazor WebAssembly はサーバー側実装を必要としないクライアント Web アプリケーションですから、Amazon S3 や GitHub Pages をはじめ、静的コンテンツを配信できる Web サーバーであれば配置可能、という利点があります。しかし一方で、そういった静的コンテンツのみを配信可能な Web サーバーでは一般的に、コンテンツを自動圧縮する機能は提供されていないように見受けられます。

Amazon S3 からの配信である限り、コンテンツの圧縮によるダウンロードサイズの縮小は諦めるしかないのでしょうか。

## Blazor のブートローダーで頑張れる!

実は Blazor WebAssembly の発行時、主立ったコンテンツファイルは事前に Brotli 圧縮されたものが同時に出力されています。`dotnet publish` コマンドの出力先フォルダを見ると、オリジナルのファイルに加えて、拡張子に `.gz` が追加されたファイルと `.br` が追加されたファイルの、2つのファイルが増えていることにお気づきでしたでしょうか? 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/7092210d-0ecd-a496-f724-3e38344b3150.png)
既にお察しと思いますが、この、拡張子に `.br` が付け加えられたファイルが、もとの名前のファイルを Brotli 圧縮したファイルです (ちなみに、`.gz` が付いているファイルは GZip 圧縮されたもの)。そして、これが大事なのですが、**実はこれら Brotli 圧縮された `.br` ファイルを読み取るように、Blazor WebAseembly の起動処理をカスタマイズできる**のです!

具体的なやり方は、しっかり、下記公式ドキュメントサイトに載っています。

https://learn.microsoft.com/ja-jp/aspnet/core/blazor/host-and-deploy/webassembly#compression

すなわち、

- Brotli 圧縮を解くデコーダー JavaScript を入手して Web コンテンツルートに追加配置しておき、
- ロード対象のファイルの名前末尾に `.br` を付け加えたファイルを fetch、
- 先のデコーダーを使って圧縮を解いて Blazor WebAssembly ブートローダに引き渡す、
- そういう JavaScript コードを `index.html` に書き足す

という改造を施すことで、実行時の通信圧縮機能のない Web サーバー上であっても、発行時に事前圧縮済みのコンテンツをダウンロードする、そんな Blazor WebAssembly アプリケーションが実現します。

## Mark2 のソースコードで試してみた!

ということで、オープンソースとなった Mark2 ですから、ソースコード一式を GitHub リポジトリから clone し、上記公式ドキュメントサイトに記載の手順に則って、`.br` ファイルを読み込むように実装して、はたしてどの程度、ダウンロードサイズが小さくなるか試してみました。

その結果はなんと...

![Brotli Loader を入れた後png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/9d776960-1cee-83b0-32c1-14f4b0fd3fcd.png)

ダウンロードサイズが **20 MB 程**にまで小さくなりました! もともとのダウンロードサイズは 100 MB 超だったので、**1/5 にまで削減**されたことになります。これは効果絶大ではないでしょうか!?

## PWA の場合は `index.html` のハッシュ値が変わることに注意点

もしも PWA として Blazor WebAssembly アプリケーションプロジェクトを構成している場合は、ちょっと注意が必要です。まず先の説明のとおり、この `.br` ファイルを読み込むようにする変更は、`index.html` ファイルの変更を伴います。しかし、`.br` ファイルが生成されるのは発行時だけであり、開発中は生成されません (圧縮するのに時間がかかるので、開発時ビルドするたびに毎回圧縮していたら、ビルド時間が長くなって開発効率が落ちるのです)。もしも開発中の時点で `index.html` が `.br` ファイルを読み込むように構成されていたら、開発中はその Blazor WebAssembly アプリは起動失敗してしまいます。ですので、`.br` ファイルを読み込むように `index.html` を構成するのは、普通に考えると、発行後の発行先フォルダに配置された `index.html` を編集・変更することになるでしょう。

ここで PWA のときに問題になるのが、発行後の `index.html` のファイルハッシュ値が、`.br` を読み込むようにする変更によって変わってしまう点です。既定の構成で作られた Blazor WebAssembly の PWA において、Service Worker がコンテンツをロードする際、`service-worker-assets.js` に記載のハッシュ値であることが検証されます。そのため、`index.html` を変更しただけで `service-worker-assets.js` (に記載のハッシュ値) をほったらかしにしていると、このハッシュ値検証が失敗して、コンテンツロードがエラーで停止してしまいます。

ということで、発行後の `index.html` を変更した場合は、変更後のファイルハッシュ値を掲載するよう `service-worker-assets.js` も手直しする必要があります。この点はくれぐれもご注意ください。

なお、GitHub Pages に配置する場合は、下記、"PublishSPAforGitHubPages.Build" NuGet パッケージを利用すると簡単です。この NuGet パッケージをプロジェクトに追加して発行すると、GitHub Pages への配置用に様々な必要な調整を行なってくれるのですが、加えて、`.br` ファイルを読み込むようにも構成してくれます。さらに、その Blazor WebAssembly アプリケーションプロジェクトが PWA として構成されている場合は、自動で `service-worker-assets.js` 内のハッシュ値の更新も自動で行なってくれます。要するに、開発者は手間を掛けることなく、この NuGet パッケージを追加するだけで、PWA であろうと、正しく Brotli 圧縮コンテンツをダウンロードするように発行できます (あくまでも GitHub Pages 用に発行するついでとしてなので、他のサーバー向けに発行する際には使えないですが)。

https://www.nuget.org/packages/PublishSPAforGitHubPages.Build

## まとめ

Blazor WebAssembly アプリケーションを Web サーバー上に設置する際は、ぜひ、サーバーからブラウザへのコンテンツ類のダウンロード通信に **Brotli 圧縮**が効くように構成しておきましょう。

HTTP 通信の圧縮機能がない・圧縮機能を構成できないサーバーに設置せざるを得ない場合は、代わりに、その Blazor WebAssembly アプリケーションのブートローダーを、**発行時に事前に Brotli 圧縮された `.br` ファイルを読み込む**ようにカスタマイズしましょう。

実際に試してみた結果として、Blazor WebAssembly 製マークシートシステム Mark2 のダウンロードサイズ **101 MB** を、**20 MB にまで削減**することができました。

Blazor WebAssembly アプリケーションのダウンロードサイズを縮小できれば、ダウンロードにかかる時間の短縮、Blazor WebAssembly アプリが起動するまでの待ち時間の短縮に寄与することでしょう (もっとも、Blazor WebAssembly のコンテンツはいちどダウンロードされたらキャッシュストレージに保存されるので、2回目以降のアクセスではダウンロードは発生しませんが...)。

_Learn, Practice, Share!_