ASP.NET Core Blazor で画像を使用する

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の ASP.NET Core 8.0 バージョンを参照してください。

この記事では、Blazor アプリで画像を使用する一般的なシナリオについて説明します。

この記事を通して、アプリ コードが実行される場所を区別するために、サーバー/サーバー側クライアント/クライアント側という用語が使用されます。

  • サーバー/サーバー側: Blazor Web アプリの対話型サーバー側レンダリング (対話型 SSR)。
  • クライアント/クライアント側
    • Blazor Web アプリのクライアント側レンダリング (CSR)。
    • Blazor WebAssembly アプリ。

ドキュメント コンポーネントの例では、通常、対話型レンダリング モードの構成は、コンポーネントの定義ファイル (.razor) 内の @rendermode ディレクティブを使用しては行いません。

  • Blazor Web アプリにおいて、コンポーネントには、コンポーネントの定義ファイル内で、または親コンポーネントから継承された対話型レンダリング モードが適用されている必要があります。 詳細については、「ASP.NET Core Blazor レンダリング モード」を参照してください。

  • スタンドアロン Blazor WebAssembly アプリでは、コンポーネントは示されているように機能し、Blazor WebAssembly アプリ内の WebAssembly 上で常に対話形式で実行されるため、レンダリング モードは必要ありません。

対話型 WebAssembly モードまたは対話型自動レンダリング モードを使用すると、クライアントに送信されたコンポーネント コードを逆コンパイルして検査できます。 プライベート コード、アプリ シークレット、またはその他の機密情報をクライアント側でレンダリングされるコンポーネントに置かないでください。

  • サーバー/サーバー側
    • ホスト型 Blazor WebAssembly アプリの Server プロジェクト。
    • Blazor Server アプリ。
  • クライアント/クライアント側
    • ホスト型 Blazor WebAssembly アプリの Client プロジェクト。
    • Blazor WebAssembly アプリ。

ファイルとフォルダーの目的と場所に関するガイダンスについては、「ASP.NET Core Blazor プロジェクトの構造」をご参照ください。参照先では、Blazor 開始スクリプトの場所と、<head><body> コンテンツの場所についても説明されています。

デモ コードを実行する最善の方法は、ターゲットの .NET のバージョンと一致する Blazor サンプル GitHub リポジトリから、BlazorSample_{PROJECT TYPE} サンプル アプリをダウンロードすることです。 現時点では、すべてのドキュメント例が同じサンプル アプリに含まれているわけではありませんが、現在 .NET 8 の記事の例のほとんどを .NET 8 サンプル アプリへと移行する作業が行われています。 この作業は、2024 年の第 1 四半期に完了する予定です。

画像のソースを動的に設定する

次の例では、C# のフィールドで画像のソースを動的に設定する方法を示します。

このセクションの例では、次のことを行います。

  • 任意のソースから 3 つの画像を取得するか、次の各画像を右クリックしてそれらをローカル環境に保存します。 画像に image1.pngimage2.pngimage3.png という名前を付けます。

    Computer iconSmiley iconEarth icon

  • アプリの Web ルート (wwwroot) の images という名前の新しいフォルダーに、画像を配置します。 images フォルダーは、デモンストレーションのためにのみ使います。 wwwroot フォルダーから直接画像を提供するなど、任意のフォルダー レイアウトに画像を整理できます。

次の ShowImage1 コンポーネントでは、以下のことを行います。

  • 画像のソース (src) は、C# の imageSource の値に動的に設定されます。
  • ShowImage メソッドは、渡された画像の id 引数に基づいて imageSource フィールドを更新します。
  • レンダリングされたボタンにより、images フォルダーで使用可能な 3 つの各画像の画像引数を指定して、ShowImage メソッドが呼び出されます。 ファイル名は、メソッドに渡された引数を使って構成され、images フォルダー内の 3 つの画像のいずれかと一致します。

ShowImage1.razor:

@page "/show-image-1"

<PageTitle>Show Image 1</PageTitle>

<h1>Show Image Example 1</h1>

@if (imageSource is not null)
{
    <p>
        <img src="@imageSource" />
    </p>
}

@for (var i = 1; i <= 3; i++)
{
    var imageId = i;
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}

@code {
    private string? imageSource;

    private void ShowImage(int id)
    {
        imageSource = $"images/image{id}.png";
    }
}
@page "/show-image-1"

<h1>Dynamic Image Source Example</h1>

@if (imageSource is not null)
{
    <p>
        <img src="@imageSource" />
    </p>
}

@for (var i = 1; i <= 3; i++)
{
    var imageId = i;
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}

@code {
    private string? imageSource;

    private void ShowImage(int id)
    {
        imageSource = $"images/image{id}.png";
    }
}
@page "/show-image-1"

<h1>Dynamic Image Source Example</h1>

@if (imageSource is not null)
{
    <p>
        <img src="@imageSource" />
    </p>
}

@for (var i = 1; i <= 3; i++)
{
    var imageId = i;
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}

@code {
    private string? imageSource;

    private void ShowImage(int id)
    {
        imageSource = $"images/image{id}.png";
    }
}

前の例では、C# のフィールドを使用して画像のソース データを保持していますが、C# のプロパティを使用してデータを保持することもできます。

Note

前の for ループの例の i など、ラムダ式内で直接ループ変数を使用することは避けてください。 そうしないと、すべてのラムダ式で同じ変数が使用され、すべてのラムダで同じ値が使用されることになります。 変数の値をローカル変数に取得してください。 前の例の場合:

  • ループ変数 iimageId に割り当てられます。
  • imageId はラムダ式で使用されます。

または、foreach ループと Enumerable.Range を使います。このようにすると上記の問題は発生しません。

@foreach (var imageId in Enumerable.Range(1,3))
{
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}

詳細については、「ASP.NET Core Blazor のイベント処理」を参照してください。

画像データのストリーミング

パブリック URL で画像をホストする代わりに、Blazor のストリーミング相互運用機能を使って、画像をクライアントに直接送信できます。

このセクションの例では、JavaScript (JS) の相互運用を使って、画像ソースのデータをストリーミングします。 次の setImageJS 関数は、画像の <img> タグ id とデータ ストリームを受け取ります。 この関数は次の手順を実行します。

  • 指定されたストリームを ArrayBuffer に読み取ります。
  • Blob を作成して、ArrayBuffer をラップします。
  • 表示するイメージのアドレスとなるオブジェクト URL を作成します。
  • 指定された imageElementId と作成したオブジェクト URL を使用して、<img> 要素を更新します。
  • メモリ リークを防ぐため、コンポーネントによる画像の使用が終わったら、関数で revokeObjectURL を呼び出してオブジェクトの URLを破棄します。
<script>
  window.setImage = async (imageElementId, imageStream) => {
    const arrayBuffer = await imageStream.arrayBuffer();
    const blob = new Blob([arrayBuffer]);
    const url = URL.createObjectURL(blob);
    const image = document.getElementById(imageElementId);
    image.onload = () => {
      URL.revokeObjectURL(url);
    }
    image.src = url;
  }
</script>

注意

JS の場所と実稼働アプリの推奨事項に関する一般的なガイダンスについては、「ASP.NET Core Blazor JavaScript の相互運用性 (JS 相互運用)」を参照してください。

次の ShowImage2 コンポーネントでは、次を実行します。

  • System.Net.Http.HttpClient および Microsoft.JSInterop.IJSRuntime に対してサービスを挿入します。
  • 画像を表示するための <img> タグを含みます。
  • 画像の Stream を取得する GetImageStreamAsync C# メソッドがあります。 運用アプリでは、特定のユーザーに基づいて画像を動的に生成したり、ストレージから画像を取得したりできます。 次の例では、dotnet GitHub リポジトリの .NET アバターを取得します。
  • ユーザーがボタンを選ぶとトリガーされる SetImageAsync メソッドがあります。 SetImageAsync では次の手順が実行されます。
    • GetImageStreamAsync から Stream を取得します。
    • 画像データをクライアントにストリーミングできるように、StreamDotNetStreamReference でラップします。
    • クライアント上のデータを受け取る setImage JavaScript 関数を呼び出します。

Note

サーバー側アプリは専用の HttpClient サービスを使って要求を行うので、開発者は HttpClient サービスを登録するためにサーバー側 Blazor アプリで何も行う必要はありません。 クライアント側アプリには、Blazor プロジェクト テンプレートからアプリが作成されるときに、既定の HttpClient サービス登録があります。 HttpClient サービス登録がクライアント側アプリの Program ファイルに存在しない場合は、builder.Services.AddHttpClient(); を追加することによって提供します。 詳細については、「ASP.NET Core で IHttpClientFactory を使用して HTTP 要求を行う」を参照してください。

ShowImage2.razor:

@page "/show-image-2"
@inject HttpClient Http
@inject IJSRuntime JS

<PageTitle>Show Image 2</PageTitle>

<h1>Show Image Example 2</h1>

<p>
    <img id="image" />
</p>

<button @onclick="SetImageAsync">
    Set Image
</button>

@code {
    private async Task<Stream> GetImageStreamAsync()
    {
        return await Http.GetStreamAsync(
            "https://avatars.githubusercontent.com/u/9141961");
    }

    private async Task SetImageAsync()
    {
        var imageStream = await GetImageStreamAsync();
        var dotnetImageStream = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setImage", "image", dotnetImageStream);
    }
}
@page "/show-image-2"
@inject HttpClient Http
@inject IJSRuntime JS

<h1>Stream Image Data Example</h1>

<p>
    <img id="image" />
</p>

<button @onclick="SetImageAsync">
    Set Image
</button>

@code {
    private async Task<Stream> GetImageStreamAsync()
    {
        return await Http.GetStreamAsync(
            "https://avatars.githubusercontent.com/u/9141961");
    }

    private async Task SetImageAsync()
    {
        var imageStream = await GetImageStreamAsync();
        var dotnetImageStream = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setImage", "image", dotnetImageStream);
    }
}
@page "/show-image-2"
@inject HttpClient Http
@inject IJSRuntime JS

<h1>Stream Image Data Example</h1>

<p>
    <img id="image" />
</p>

<button @onclick="SetImageAsync">
    Set Image
</button>

@code {
    private async Task<Stream> GetImageStreamAsync()
    {
        return await Http.GetStreamAsync(
            "https://avatars.githubusercontent.com/u/9141961");
    }

    private async Task SetImageAsync()
    {
        var imageStream = await GetImageStreamAsync();
        var dotnetImageStream = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setImage", "image", dotnetImageStream);
    }
}

その他のリソース