😸

BlazorWebAssembly + OpenXMLでExcelファイルを生成する - C#

に公開

概要

ブラウザ上でExcel操作をするライブラリといえばSheetJsや、ExcelJSです。

しかし、どうせなら公式というのにこだわりたいところです。

さて、Microsoft謹製のExcel操作のライブラリといえばOpenXMLです。

Blazorならそんなこだわりも叶えることができます。

前提

  • WSL Ubuntu 22.04.5 LTS (Jammy Jellyfish)
  • dotnet 8.0.112

コード

この記事のコードは以下に配置しています。
https://github.com/Kuroki-g/kuroki-g-public-zenn-code/tree/main/

.NETプロジェクトの作成

ひな形の作成

ざっとプロジェクトを作ります。

$ dotnet new blazorwasm --name dotnet-blazorwasm-openxml
$ dotnet new gitignore
$ dotnet new editorconfig

ライブラリの追加

NugetのDocumentFormat.OpenXmlで最新版を確認して依存関係に追加します。

$ dotnet add package DocumentFormat.OpenXml --version 3.3.0

BlazorWebAssemblyでのExcel生成コードの作成

ページの作成

適当にひな形のCounter.razorをコピーして作ります。

Excel.razor

@page "/excel"

<PageTitle>Excel</PageTitle>

<h1>Excel</h1>

<button class="btn btn-primary" @onclick="GenerateSheet">Click me</button>

@code {
    private void GenerateSheet()
    {
        // Generate code here!
    }
}

シートの生成

OpenXMLはかなり使いにくいので、コードサンプルを用意するのも面倒です。
なので、先人の遺産を参考にしましょう。

今回は適当なシートを作成するクラスを作成しました。

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Packaging;

namespace Models;

sealed class SampleBook : IDisposable
{
    private SampleBook(string filePath)
    {
        _document = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook);
        _workbookPart = _document.AddWorkbookPart();
        _workbookPart.Workbook = new Workbook();
        _sheets = _workbookPart.Workbook.AppendChild<Sheets>(new Sheets());

        // シート作成のための準備
        _worksheetPart = _workbookPart.AddNewPart<WorksheetPart>();
        _worksheetPart.Worksheet = new Worksheet(new SheetData());

    }

    private SpreadsheetDocument _document;

    private WorkbookPart _workbookPart;

    private WorksheetPart _worksheetPart;

    private Sheets _sheets;

    /// <summary>
    /// サンプルExcelのBookを新規作成する。
    /// </summary>
    /// <param name="filePath"></param>
    /// <returns></returns>
    public static SampleBook CreateBook(string filePath) {
        var book = new SampleBook(filePath);
        book.CreateSheet("シート1");

        return book;
    }

    public void CreateSheet(string sheetName)
    {

        // シートを作成し追加する
        var max = (uint)(_sheets.Count() + 1);
        var sheet = new Sheet {
            Id = _workbookPart.GetIdOfPart(_worksheetPart),
            SheetId = max,
            Name = sheetName
        };
        _sheets.Append(sheet);
    }

    /// <summary>
    /// 現在のbookを保存し、streamに渡すためのバイト列を生成する。
    /// </summary>
    public async Task<byte[]> SaveAndGetBytesAsync()
    {
        _workbookPart.Workbook.Save();
        
        using var stream = new MemoryStream();
        _document.Clone(stream);

        return stream.ToArray();
    }

    /// <summary>
    /// Book全体の保存を行う。
    /// </summary>
    public void Save() {
        _workbookPart.Workbook.Save();
    }
    
    private bool _isDisposed = false;

    public void Dispose() => Dispose(true);   

    private void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            if (disposing)
            {
                _document?.Dispose();
            }
            _isDisposed = true;
        }
    }
}

ダウンロード用関数の追加

JavaScriptコードを作成します。

function DownloadFile(filename, contentType, content) {
    // Create a new Blob object from the byte array
    const data = new Uint8Array(content);
    const blob = new Blob([data], { type: contentType });

    // Create a URL for the blob
    const url = window.URL.createObjectURL(blob);

    // Create a temporary anchor element and trigger the download
    const a = document.createElement('a');
    a.href = url;
    a.download = filename; // Set the filename
    document.body.appendChild(a);
    a.click();

    // Clean up
    document.body.removeChild(a);
    window.URL.revokeObjectURL(url);
}

BlazorからJavaScriptの、ダウンロード用コードを呼び出せるようにします。
ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出すを参考に。

そして適当にExcelブックを生成してダウンロードするためのJavaScript関数を呼び出します。

@using Models
@inject IJSRuntime JS
@code {
    private async void GenerateExcel()
    {
        Console.WriteLine("called GenerateExcel");
        
        using var book = SampleBook.CreateBook("example.xlsx");
        book.CreateSheet("シート2");
        book.Save();

        byte[] fileBytes = await book.SaveAndGetBytesAsync();
        
        await JS.InvokeVoidAsync(
            "DownloadFile",
            "example.xlsx",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            fileBytes
        );
    }
}

ブラウザ側からの挙動の確認

ローカル開発環境用サーバを立ち上げ、ブラウザから http://localhost:5043/excel にアクセスします。

dotnet run

ブラウザの画面

ボタンを押してダウンロードできることを確認します。

ダウンロードの確認

参考文献

GitHubで編集を提案

Discussion