インフラジスティックス・ジャパン株式会社Blog

インフラジスティックス・ジャパン株式会社のチームメンバーが技術トレンド、製品Tips、サポート情報からライセンス、日々の業務から感じることなど、さまざまなトピックについてお伝えするBlogです。

Blazor WebAssembly 上で Excel ファイルを読み書きし、サーバー負荷を減らす

こんにちは、インフラジスティックス・ジャパンです。
本記事では Infragistics Blazor Excel ライブラリの活用についてご紹介します。

Blazor WebAssembly アプリケーション上で Infragistics Blazor Excel ライブラリを使うと、Web ブラウザ上で Excel 形式のファイル (.xlsx) を生成し、ダウンロードさせることができるようになります。

この方法で Excel ファイルを生成すると、その処理は Web サーバー側ではなく Web ブラウザ上で行なわれますので、Excel 生成処理の負荷がサーバーに一極集中しない、という利点があります。

この記事では、サンプルアプリケーションの作成を通してその実装方法、および、そこからさらに一歩踏み込んだ話題を取り上げていきます。

作成するサンプルアプリケーション

インターネット上の気象庁防災情報 XML サービスから取得したデータに基づき、直近7日間に発生した1日ごとの地震発生回数を、Excel の表にしてダウンロードできる Blazor WebAssembly アプリケーションを作ってみます。ダウンロードされる Excel にはバーチャートを組み込み済みとしてみました。

なお、このサンプルアプリケーションのすべてのソースコードについては GitHub リポジトリ上に公開してあり、本ブログ記事の最後の「まとめ」のところにてその URL をご紹介しております。

実装手順

プロジェクト構造の概要

気象庁防災情報 XML を取得するにあたって CORS の関係上、スタンドアロンの Blazor WebAssembly プロジェクトではなく、ASP.NET Core サーバーでホストされる Blazor WebAssembly プロジェクトとします。ただし冒頭に記したとおり、Excel ファイル生成はすべて Web ブラウザ上で完結して処理します。ASP.NET Core サーバー側は、気象庁防災情報 XML を取得・加工して Web ブラウザ上で実行されている Blazor WebAssembly プログラムに JSON でデータ提供するためだけに必要です。

また、生成する Excel ファイルはプログラムコード上で全くの新規に生成するのではなく、予めバーチャートを構成した「ひな形 (テンプレート)」となる Excel ファイル (.xlsx) を Web サーバー上に配置しておき、これを Blazor WebAssembly プログラム上で HttpClient を使って取得、この Excel ファイル中のシートの各セルを日付と地震発生回数で埋めていく方式とします。

ということで、気象庁防災情報 XML から取得し加工した直近7日間の1日ごと地震発生回数を、Ignite UI for Blazor のグリッドに表示できるように実装できたところから、Excel ファイルを生成する処理を追加していく手順を以下に記していきます。

NuGet パッケージの追加

Infragistics Blazor Excel ライブラリの利用には、NuGet パッケージ「IgniteUI.Blazor.Documents.Excel」が必要です。

Visual Studio をお使いでしたら NuGet パッケージマネージャーを使うなどして、Blazor WebAssembly プロジェクトに NuGet パッケージ「IgniteUI.Blazor.Documents.Excel」を追加しておきます。

および、このあと記述するプログラムコードにて名前空間の記述を簡略化するため、_Imports.razor に Infragistics Blazor Excel ライブラリの名前空間 Infragistics.Documents.Excel を予め開いておくことにします。

@* _Imports.razor *@
@using System.Net.Http
...
@using Infragistics.Documents.Excel @* 👈 この行を追加 *@

JavaScript ファイルへの参照の追加

Blazor WebAssembly 上で Infragistics Blazor Excel ライブラリによる Excel ファイルの操作を行なうには、NuGet パッケージ「IgniteUI.Blazor.Documents.Excel」によって提供される JavaScript ファイルを Web ページ上で読み込んでおく必要があります。

Blazor WebAssembly プロジェクト中のフォールバックページである wwwroot/index.html ファイル中に、下記のように <script> タグを追加します。

    ....
    <script src="_content/IgniteUI.Blazor/app.bundle.js"></script>

    <!-- 👇 この行を追加 -->
    <script src="_content/IgniteUI.Blazor.Documents.Excel/excel.js"></script>

    <script src="_framework/blazor.webassembly.js"></script>
  </body>
</html>

Workbook.InProcessRuntime 静的プロパティを設定

以上で Infragistics Blazor Excel ライブラリを使う準備ができました。 Excel ファイルの読み書きには、具体的には、Infragistics.Documents.Excel 名前空間の Workbook クラスを使用します。

なお、ひとつ注意点として、Blazor WebAssembly 上で Workbook クラスを使って Excel ファイルを処理するにあたり、事前にいちど、Workbook.InProcessRuntime 静的プロパティに IJSInProcessRuntime インターフェースを持つ JavaScript ランタイムオブジェクトを設定してやる必要があります。
下記は Razor コンポーネント上での実装例の抜粋です。

@inject IJSRuntime JSRuntime
...
@code {
    ...
    // ⚠️注意 - Blazor WebAssembly 上で Excel ライブラリを使うには、
    //          Workbook.InProcessRuntime 静的プロパティの初期設定が必要です。
    if (Workbook.InProcessRuntime == null) 
        Workbook.InProcessRuntime = this.JSRuntime as IJSInProcessRuntime;
    ...

Workbook クラスを使って Excel ファイルを読み書き

ひとたびここまで準備ができたら、あとは、Workbook クラスの Load() 静的メソッドを使って Excel ファイルを読み込み、返ってきた Workbook オブジェクトを介してシートやセルのオブジェクトを参照したり値を書き込んだりすることができます。
ひととおり Excel 表の作成が済んだら、最後に Workbook オブジェクトの Save() メソッドを使って Stream に Excel ファイルとしてのコンテンツを書き込むことができますので、.NET の MemoryStream に書き込んで byte の配列を取得し、これをそのままブラウザにダウンロードさせることができます。
(Blazor アプリケーション上でのダウンロード発動処理は、MIT ライセンスの OSS ライブラリを利用しました)

以下は Razor コンポーネント上での実装例の抜粋です。

@inject HttpClient HttpClient
@inject IJSRuntime JSRuntime
...
@code
{
    ...
    private IEnumerable<EarthquakeCountParDay>? _EarthquakeCountParDays;
    ...
    /// <summary>
    /// [ダウンロード] ボタンがクリックされたときに呼び出され、直近1週間の1日ごと地震発生回数データを Excel ファイルに収めてダウンロードさせます。
    /// </summary>
    private async Task OnClickedDownloadAsync()
    {
        // ⚠️注意 - Blazor WebAssembly 上で Excel ライブラリを使うには、Workbook.InProcessRuntime 静的プロパティの初期設定が必要です。
        if (Workbook.InProcessRuntime == null) Workbook.InProcessRuntime = this.JSRuntime as IJSInProcessRuntime;

        // 雛形の Excel ファイルをサーバーから取得し、Workbook オブジェクトに読み込みます。
        await using var templateStream = await this.HttpClient.GetStreamAsync("./TemplateBook.xlsx");
        var workBook = Workbook.Load(templateStream);
        await templateStream.DisposeAsync();

        // シートの中身 (セル) に、地震発生の日付と回数を転記していきます。
        var sheet = workBook.Worksheets[0];
        var row = 2;
        foreach (var item in this._EarthquakeCountParDays)
        {
            sheet.GetCell($"A{row}").Value = item.Date;
            sheet.GetCell($"B{row}").Value = item.Count;
            row++;
        }

        // 記入が終わった Workbook オブジェクトを .xlsx ファイル形式に書き出し、ブラウザにダウンロードさせます。
        await using var memStream = new MemoryStream();
        workBook.Save(memStream);
        await this.JSRuntime.InvokeDownloadAsync("Book.xlsx", "application/octet-stream", memStream.ToArray());
    }
    ...

本記事では Excel ファイルの読み書きの詳細については割愛させて頂きますが、上記コード抜粋のコメント、および、下記リンク先のヘルプドキュメントを参照いただければと思います。

jp.infragistics.com

以上の手順にて、Blazor WebAssembly アプリケーション上で、ひな形となる Excel ファイルのセルを実データで埋めてダウンロードさせる処理が実現できました。

Excel ファイルの加工処理は Web ブラウザ上で行なわれ、Web サーバー側では何も処理が発生しませんので、多数のクライアントが一斉に Excel ファイル生成を実行しても Web サーバー側の負荷が急激に上がるような事態を避けることができます。

処理に時間がかかってしまう場合

Excel ファイルの処理は、その内容や、取り扱うデータの規模・セル数などによっては、随分と時間がかかってしまう場合があります。とくに、インタープリター方式で .NET コードが処理される Blazor WebAssembly アプリケーション上では、その処理性能の劣化が顕著です。そのような場合に、処理速度を改善できるかもしれない選択肢をいくつかご紹介します。

数式の自動計算を一時停止する

Blazor WebAssembly アプリケーション上で Excel ファイル中のセル参照や書き換えにとても時間がかかってしまう場合、もしかすると、数式の自動計算・再計算に負荷がかかっているかもしれません。そのような場合、Infragistics Blazor Excel ライブラリでは、数式の自動計算を一時停止できますので、そうすることで処理速度の改善が見込まれます。詳しくは INFRAGISTICS ナレッジベース (下記リンク先) をご参照ください。

kb.jp.infragistics.com

Ahead-Of-Time (AOT) コンパイルを使う

.NET 6 以降では、Blazor WebAssembly アプリケーション構築に「Ahead-Of-Time (AOT) コンパイル」という技法が使えます。これは、アプリケーションの発行時に .NET コードを直接 WebAssembly にコンパイルし、処理速度の向上を図る技法です。AOT コンパイルの詳細は、下記リンク先のマイクロソフトの公式ドキュメントサイトをご参照ください。

docs.microsoft.com

AOT コンパイルは、発行時のみとはいえ分単位でコンパイルに時間がかかるであるとか、生成されるコンテンツサイズが倍以上に膨らむ、アプリケーション全体では処理速度の向上が認められない場合があるなど、必ずしも Blazor WebAssembly アプリケーション構築における最適解ではないため、オプションとなっています。
しかしながら、当方の手元での、Infragistics Blazor Excel ライブラリを使ったとある Excel ファイル読み書きのベンチマークでは、標準の .NET 6 Blazor WebAssembly アプリケーションで 8 秒かかっていた Excel ファイルの処理が、AOT コンパイルが有効だと 4 秒で済むように改善されました。さらに、本ブログ記事執筆時点ではまだプレビュー版である .NET 7 を使って AOT コンパイルしたところ 2 秒で処理完了するようになりました。

サーバ側で実行する

このブログ記事の主題は、Web ブラウザ上で Excel ファイルの処理を行なうことでサーバー負荷を軽減する話であるため、その観点では本末転倒なのですが、サーバー負荷は問題ではなく Excel ファイル生成処理時間のほうが問題であるケースも多々あろうかと思います。そのような場合は、Excel ファイルの生成処理をサーバー側で行なう選択肢も考えられます。インタープリター方式で .NET コードが逐次処理される Blazor WebAssembly と異なり、Blazor Server や ASP.NET Core サーバー上での Infragistics Blazor Excel ライブラリの実行は、Just In Time (JIT) コンパイラによって CPU ネイティブな速度で実行されますので、圧倒的な処理速度の改善が見込まれます。

アプリケーションをまるごと Blazor WebAssembly から Blazor Server に作り替えることでももちろん構いませんし、基本的には Blazor WebAssembly アプリケーションのまま、Excel ファイル生成処理部分だけを ASP.NET Core サーバーの Web API に載せ替えることでも実現可能です。なお、Infragistics Blazor Excel ライブラリは、その名前に "Blazor" と記載されていますが、そもそも Blazor Server は ASP.NET Core の1機能であることもあり、ASP.NET Core サーバーの API コントローラなどでも利用可能です。

同じコード・同じアセンブリで実装した Excel ファイルの読み書き処理が、実測した結果の処理性能や要件に応じて、サーバー側とクライアント側どちらにも自在に配置できるのが Blazor の強みであると言えるでしょう。

なお、一点だけ、Infragistics Blazor Excel ライブラリの Linux サーバー上での利用には注意が必要です。詳しくは INFRAGISTICS ナレッジベース (下記リンク先) をご参照ください。

kb.jp.infragistics.com

"アセンブリの遅延読み込み" によりアプリケーションの初速を改善

Infragistics Blazor Excel ライブラリのアセンブリファイルのサイズ

Infragistics Blazor Excel ライブラリが提供するアセンブリファイル (.dll) のサイズは、概ね 5 MB ほどあります。初回起動時のコンテンツサイズが膨らむと、コンテンツの読み込み時間・Blazor WebAssembly アプリケーションが操作可能になるまでの初速時間が問題になる場合が想像されます。

そのような場合は、.NET 5 以降で使える「アセンブリの遅延読み込み」という技法を使うことで、Infragistics Blazor Excel ライブラリが提供するアセンブリファイル (.dll) の読み込みを、Excel ファイルの読み書き処理を行なうそのときまで先送りすることで、Blazor WebAssembly アプリケーションの初期起動時間を改善できる場合があります。「アセンブリの遅延読み込み」について詳細は、下記リンク先のマイクロソフトの公式ドキュメントサイトをご参照ください。

docs.microsoft.com

せっかくですので、試しに今回ご紹介のサンプルアプリケーションに、この「アセンブリの遅延読み込み」を適用してみましょう。

プロジェクトファイル中で遅延読み込みするアセンブリを指定する

まずは Blazor WebAssembly のプロジェクトファイル (.csproj) にて、<ItemGroup> 要素内に <BlazorWebAssemblyLazyLoad> 要素を並べて、遅延読み込みさせたいアセンブリファイル (.dll) の名前を列記します。Infragistics Blazor Excel ライブラリが提供するアセンブリファイルを遅延読み込みさせる例が下記となります。

<!-- Blazor WebAssembly のプロジェクトファイル (.csproj) -->

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
  ...
  <!-- 遅延読み込みさせたいアセンブリファイル (.dll) のファイル名を、
         BlazorWebAssemblyLazyLoad 要素で指定します。 -->
  <ItemGroup>
    <BlazorWebAssemblyLazyLoad Include="IgniteUI.Blazor.Documents.Core.dll" />
    <BlazorWebAssemblyLazyLoad Include="IgniteUI.Blazor.Documents.Excel.dll" />
  </ItemGroup>
  ...
</Project>

LazyAssemblyLoader を使ってアセンブリを読み込む

続きまして、"遅延読み込みする" と指定したアセンブリを、プログラム上からその必要が発生するタイミングで読み込むよう実装します。アセンブリの読み込みには、Blazor WebAssembly に標準搭載されている LazyAssemblyLoader クラスのサービスを DI コンテナから注入してもらって、その LoadAssembliesAsync() 非同期メソッドを呼び出すことで行ないます。
今回のサンプルアプリケーションでは、Excel ファイルのダウンロードボタンがクリックされたときに、Infragistics Blazor Excel ライブラリのアセンブリファイルを読み込むようにします。下記はその抜粋です。

@using Microsoft.AspNetCore.Components.WebAssembly.Services
...
@inject LazyAssemblyLoader AssemblyLoader

@code {
    ...
    /// <summary>
    /// [ダウンロード] ボタンがクリックされたときに呼び出され、直近1週間の1日ごと地震発生回数データを Excel ファイルに収めてダウンロードさせます。
    /// </summary>
    private async Task OnClickedDownloadAsync()
    {
        // プロジェクトファイル (IgbExcelDemo.Client.csproj) にて遅延読み込みを指定しておいた
        // アセンブリファイル (.dll) を、ここで AssemblyLoader を使って読み込みます。
        // (補足: 繰り返し何度も呼び出しても大丈夫です、まだいちども読み込まれていない .dll のみが読み込まれます)
        await this.AssemblyLoader.LoadAssembliesAsync(new[]
        {
            "IgniteUI.Blazor.Documents.Core.dll",
            "IgniteUI.Blazor.Documents.Excel.dll"
        });
        ...

遅延読み込みするアセンブリに関係する処理を別のスコープに切り出す

さてこのまま試しにこのサンプルアプリケーションを実行すると、初回ページ表示時には、みごと期待どおり Infragistics Blazor Excel ライブラリのアセンブリファイルは読み込まれなくなり、ページ初回表示時に読み込まれるコンテンツサイズが 5MB ほど節約できたことが確認できます。

ところが、ここで実際にダウンロードボタンをクリックすると、"System.IO.FileNotFoundException: Could not load file or assembly" 例外が発生してしまいます。 これは遅延読み込みを行なっているのと同じメソッドのスコープ内でその遅延読み込みされるアセンブリ内に存在する型を参照していると、まだそのアセンブリが読み込まれていないのに、そのメソッドのスコープ内の型を解決しようとして、この例外となってしまいます。

この問題を回避するには、遅延読み込みされるアセンブリ内に存在している型 (このサンプルアプリケーションの例ですと、Workbook クラスなど) を参照している処理を、別の独立したメソッドに切り出し、そのメソッドを呼び出すようにします。下記はその抜粋です。

...
@code {
    ...
    /// <summary>
    /// [ダウンロード] ボタンがクリックされたときに呼び出され、直近1週間の1日ごと地震発生回数データを Excel ファイルに収めてダウンロードさせます。
    /// </summary>
    private async Task OnClickedDownloadAsync()
    {
        ...
        await this.AssemblyLoader.LoadAssembliesAsync(new[]
        {
            "IgniteUI.Blazor.Documents.Core.dll",
            "IgniteUI.Blazor.Documents.Excel.dll"
        });

        // アセンブリの遅延読み込みを行なってから、その遅延読み込みされるアセンブリ内の機能を使うメソッドを呼び出します。
        // (アセンブリの遅延読み込みを行なうメソッド内で直接、遅延読み込みしたアセンブリ内の機能を参照していると、
        //  System.IO.FileNotFoundException: Could not load file or assembly 例外が発生します。)
        await this.DownloadAsExcelAsync();
    }

    /// <summary>
    /// アセンブリの遅延読み込みを有効にするために、Excel ライブラリを使用するコードを独立したメソッドに切り出します。
    /// </summary>
    private async ValueTask DownloadAsExcelAsync()
    {
        // このメソッド内で Workbook クラスを使った Excel ファイルの読み書きを行ないます
    }
    ...

以上、「1. .csproj で遅延読み込みするアセンブリを指定」「2. LazyAssemblyLoader で読み込み」「3. 別メソッドで処理実行」の 3 ステップで、Infragistics Blazor Excel ライブラリのアセンブリファイルを遅延読み込みさせることができました。

まとめ

Blazor WebAssembly アプリケーション上で Infragistics Blazor Excel ライブラリを使うことで、Web ブラウザ上で完結して Excel ファイルの読み書きを行えること、その結果として Web サーバー側の負荷を軽減できることをご紹介しました。また、処理速度上の懸念がある場合に、取り得ることのできる改善案の選択肢もいくつかご紹介させて頂きました。

本記事で紹介したサンプルアプリケーションのすべてのソースコードは、下記リンク先の GitHub リポジトリで公開してあります。Infragistics Blazor Excel ライブラリの組み込みを行なう手順を独立したコミットに分けてあるほか、アセンブリの遅延読み込みの有効化は lazy-assembly-loading ブランチにコミットしてあります。

github.com

また、Infragistics Excel ライブラリを使用するにあたり、Ignite UI for Blazor のライセンスをお持ちでない場合は、30 日間のサポート付きトライアル版にてお試しいただけます。 トライアル版のダウンロードは、下記リンク先のフォームに必要事項を記入頂くことで可能です。

🚀 Ignite UI for Blazor 無料トライアル版のダウンロード

以上、本記事が皆様のアプリケーション開発のお役に立てば幸いです。

開発全般に関するご相談はお任せください!

インフラジスティックス・ジャパンでは、各プラットフォームの特別技術トレーニングの提供や、開発全般のご支援を行っています。

  • 「古い技術やサポート終了のプラットフォームから脱却する必要があるが、その移行先のプラットフォームやフレームワークの検討が進まない、知見がない」
  • 「新しい開発テクノロジーを採用したいが、自社内にエキスパートがいない。日本語リソースも少ないし、開発を進められるか不安」
  • 「自社のメンバーで開発を進めたいが、これまで開発フェーズを外部ベンダーに頼ってきたため、ツールや技術に対する理解が乏しい」
  • 「UIを刷新したい。UIデザインやUI/UXに関する検討の進め方が分からない。外部のデザイン会社に頼むと、開発が難しくなるのではないか、危惧している」

といったご相談を承っています。

お問い合わせはこちらから

お問い合わせフォームをご用意しております。ぜひお気軽にご連絡ください。

jp.infragistics.com