SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

マルチターゲットアプリ開発の新しいアプローチ~.NET 9 新テンプレートの基本~

.NET MAUI Blazorで生成した画像を保存する。ネイティブ機能との連携

マルチターゲットアプリ開発の新しいアプローチ~.NET 9 新テンプレートの基本~ 第5回

Web:画像ダウンロードの実装(JS連携)

 次に、Webプロジェクトの実装です。Web環境では、ローカルにファイルを保存する場合、ブラウザの機能を使う必要があります。

IJSRuntimeとは

 今回は、ブラウザの機能を使うためにJavaScriptを利用します。BlazorからJavaScript関数を呼び出すには、IJSRuntimeインターフェイスを使用します。InvokeVoidAsyncメソッドで、戻り値のないJavaScript関数を、InvokeAsyncメソッドで、戻り値のある関数を非同期で呼び出せます。

Blazor ServerでのIJSRuntime利用時の注意

 Blazor ServerアプリケーションでIJSRuntimeを使用する際は、注意が必要です。IJSRuntimeはSignalR接続に紐づいており、サービスクラスのコンストラクタでインジェクションすると、接続のライフサイクルとの不整合でエラーが発生することがあります。

 この問題を回避するため、今回の実装では、IJSRuntimeをサービスのコンストラクタではなく、メソッドの引数として渡す設計にしています。RazorコンポーネントでインジェクションしたIJSRuntimeをメソッド呼び出し時に渡すことで、適切なタイミングでJavaScriptを実行できます。

実装クラスの作成

 ImageGenerator.WebプロジェクトのServicesフォルダに、WebImageSaveServiceクラスを新規作成します。

[リスト5]WebImageSaveService.cs
using Microsoft.JSInterop;

public class WebImageSaveService(IHttpClientFactory httpClientFactory) : IImageSaveService
{
    public string ActionLabel => "ダウンロード";  // UI文言の設定 (5)

    public async Task<bool> SaveImageAsync(string imageUrl, string fileName, object? jsRuntime = null)
    {
        // jsRuntimeがnullまたはIJSRuntimeでない場合はエラー (1)
        if (jsRuntime is not IJSRuntime js)
        {
            throw new ArgumentException( "Web環境ではIJSRuntimeが必要です");
        }

        try
        {
            // C#側で画像をダウンロード(CORS制限を回避)(2)
            using var httpClient = httpClientFactory.CreateClient();
            var imageBytes = await httpClient.GetByteArrayAsync(imageUrl);

            // Base64に変換 (3)
            var base64 = Convert.ToBase64String(imageBytes);
            var dataUrl = $"data:image/png;base64,{base64}";

            // Base64データURLをJavaScriptに渡してダウンロード (4)
            await js.InvokeVoidAsync("downloadImageFromDataUrl", dataUrl, fileName);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"画像ダウンロードエラー: {ex.Message}");
            return false;
        }
    }
}

 SaveImageAsyncメソッドの引数として受け取ったjsRuntimeを、is構文を使って型チェックとIJSRuntime型として変数宣言を行っています(1)。

 Web環境ではIJSRuntimeが必須のため、nullや異なる型の場合は例外をスローします。IJSRuntime型の場合のみ、変数jsが定義されます。そして、IHttpClientFactoryを通じて画像データをダウンロードします(2)。

 ここで重要なのは、JavaScriptの関数ではなく、C#のHttpClientを使っている点です。OpenAI APIから返される画像URLに対して、JavaScriptから直接fetchするとCORS(Cross-Origin Resource Sharing)制限によりエラーが発生します。サーバーサイドのC#コードで画像を取得することで、この制限を回避できます。

 取得した画像データはBase64文字列に変換し(3)、データURL形式でJavaScriptに渡してダウンロードを実行します(4)。また、UIの文言も定義しておきます(5)。

JavaScript関数の追加

 ImageGenerator.WebプロジェクトのComponents/App.razorに、ダウンロード用のJavaScript関数を追加します。</body>タグの直前に記述してください。

[リスト6]App.razorの一部
<script>
    // データURLから直接ダウンロードリンクを作成
    function downloadImageFromDataUrl(dataUrl, fileName) {
        const a = document.createElement('a'); 
        a.href = dataUrl;
        a.download = fileName;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }
</script>
</body>

 この関数は、Base64形式のデータURLを受け取り、動的に作成したアンカー要素を使ってダウンロードを実行します。fetchを使用しないため、CORS制限の影響を受けません。

次のページ
サービスの登録(DI)

この記事は参考になりましたか?

マルチターゲットアプリ開発の新しいアプローチ~.NET 9 新テンプレートの基本~連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 高江 賢(タカエ ケン)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/23143 2026/02/03 08:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング