ASP.NET Core Blazor でイメージとドキュメントを表示する

Note

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

警告

このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、「.NET および .NET Core サポート ポリシー」を参照してください。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

この記事では、 Blazor アプリで画像とドキュメントを表示する方法について説明します。

この記事の例は、次の Blazor サンプル アプリで検査および使用できます。

dotnet/blazor-samples GitHub リポジトリ: BlazorSample_BlazorWebApp (8.0 以降)、 BlazorSample_Server (7.0 以前)、または BlazorSample_WebAssembly という名前のアプリに移動します。

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

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

このセクションの例では、image1.pngimage2.png、および image3.png という名前の 3 つのイメージ ファイルを使用します。 アプリの 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 のストリーミング相互運用機能を使って、画像やドキュメントの種類 (PDFなど) をクライアントに直接送信できます。

このセクションの例では、JavaScript (JS) の相互運用を使って、画像ソースのデータをストリーミングします。 次の setSourceJS 関数:

  • <body><embed><iframe><img><link><object><script><style><track> の各要素のコンテンツをストリーミングするために使用できます。
  • ファイルの内容、ドキュメントのデータ ストリーム、コンテンツ タイプ、および表示要素のタイトルを表示する要素 id を受け入れます。

関数は、次のことを行います。

  • 指定されたストリームを ArrayBuffer に読み取ります。
  • BLOB のコンテンツ タイプを設定して、ArrayBuffer をラップするBlobを作成します。
  • 表示するドキュメントのアドレスとなるオブジェクト URL を作成します。
  • title パラメーターから要素のタイトル (title) を設定し、作成されたオブジェクト URL から要素のソース (src) を設定します。
  • メモリ リークを防ぐために、関数は revokeObjectURL を呼び出して、要素がリソース (load イベント) を読み込んだ後にオブジェクト URL を破棄します。
<script>
  window.setSource = async (elementId, stream, contentType, title) => {
    const arrayBuffer = await stream.arrayBuffer();
    let blobOptions = {};
    if (contentType) {
      blobOptions['type'] = contentType;
    }
    const blob = new Blob([arrayBuffer], blobOptions);
    const url = URL.createObjectURL(blob);
    const element = document.getElementById(elementId);
    element.title = title;
    element.onload = () => {
      URL.revokeObjectURL(url);
    }
    element.src = url;
  }
</script>

Note

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

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

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

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

<div class="p-3">
    <img id="avatar" />
</div>

@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 strRef = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setSource", "avatar", strRef, "image/png", 
            ".NET GitHub avatar");
    }
}
@page "/show-image-2"
@inject HttpClient Http
@inject IJSRuntime JS

<h1>Show Image Example 2</h1>

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

<div class="p-3">
    <img id="avatar" />
</div>

@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 strRef = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setSource", "avatar", strRef, "image/png", 
            ".NET GitHub avatar");
    }
}
@page "/show-image-2"
@inject HttpClient Http
@inject IJSRuntime JS

<h1>Show Image Example 2</h1>

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

<div class="p-3">
    <img id="avatar" />
</div>

@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 strRef = new DotNetStreamReference(imageStream);
        await JS.InvokeVoidAsync("setSource", "avatar", strRef, "image/png", 
            ".NET GitHub avatar");
    }
}

次の ShowFile コンポーネントは、テキスト ファイル (files/quote.txt) または PDF ファイル (files/quote.pdf) を <iframe> 要素 (MDN ドキュメント) に読み込みます。

注意事項

次の例で <iframe> 要素を使用することは安全であり、コンテンツが信頼できるソースであるアプリから読み込まれるため、[サンドボックス](https://developer.mozilla.org/docs/Web/HTML/Element/iframe#sandbox) は必要ありません。

信頼されていないソースまたはユーザー入力からコンテンツを読み込むと、不適切に実装された <iframe> 要素によってセキュリティの脆弱性が発生するリスクがあります。

ShowFile.razor:

@page "/show-file"
@inject NavigationManager NavigationManager
@inject HttpClient Http
@inject IJSRuntime JS

<PageTitle>Show File</PageTitle>

<div class="d-flex flex-column">
    <h1>Show File Example</h1>
    <div class="mb-4">
        <button @onclick="@(() => ShowFileAsync("files/quote.txt", 
                "General Ravon quote (text file)"))">
            Show text ('quote.txt')
        </button>
        <button @onclick="@(() => ShowFileAsync("files/quote.pdf", 
                "General Ravon quote (PDF file)"))">
            Show PDF ('quote.pdf')
        </button>
    </div>
    <iframe id="iframe" style="height: calc(100vh - 200px)" />
</div>

@code
{
    private async Task<(Stream, string?)> DownloadFileAsync(string url)
    {
        var absoluteUrl = NavigationManager.ToAbsoluteUri(url);
        Console.WriteLine($"Downloading file from {absoluteUrl}");

        var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

        if (response.Content.Headers.TryGetValues("Content-Type", out var values))
        {
            contentType = values.FirstOrDefault();
        }

        return (await response.Content.ReadAsStreamAsync(), contentType);
    }

    private async Task ShowFileAsync(string url, string title)
    {
        var (fileStream, contentType) = await DownloadFileAsync(url);
        var strRef = new DotNetStreamReference(fileStream);
        await JS.InvokeVoidAsync("setSource", "iframe", strRef, contentType, title);
    }
}
@page "/show-file"
@inject NavigationManager NavigationManager
@inject HttpClient Http
@inject IJSRuntime JS

<div class="d-flex flex-column">
    <h1>Show File Example</h1>
    <div class="mb-4">
        <button @onclick="@(() => ShowFileAsync("files/quote.txt", 
                "General Ravon quote (text file)"))">
            Show text ('quote.txt')
        </button>
        <button @onclick="@(() => ShowFileAsync("files/quote.pdf", 
                "General Ravon quote (PDF file)"))">
            Show PDF ('quote.pdf')
        </button>
    </div>
    <iframe id="iframe" style="height: calc(100vh - 200px)" />
</div>

@code
{
    private async Task<(Stream, string?)> DownloadFileAsync(string url)
    {
        var absoluteUrl = NavigationManager.ToAbsoluteUri(url);
        Console.WriteLine($"Downloading file from {absoluteUrl}");

        var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

        if (response.Content.Headers.TryGetValues("Content-Type", out var values))
        {
            contentType = values.FirstOrDefault();
        }

        return (await response.Content.ReadAsStreamAsync(), contentType);
    }

    private async Task ShowFileAsync(string url, string title)
    {
        var (fileStream, contentType) = await DownloadFileAsync(url);
        var strRef = new DotNetStreamReference(fileStream);
        await JS.InvokeVoidAsync("setSource", "iframe", strRef, contentType, title);
    }
}
@page "/show-file"
@inject NavigationManager NavigationManager
@inject HttpClient Http
@inject IJSRuntime JS

<div class="d-flex flex-column">
    <h1>Show File Example</h1>
    <div class="mb-4">
        <button @onclick="@(() => ShowFileAsync("files/quote.txt", 
                "General Ravon quote (text file)"))">
            Show text ('quote.txt')
        </button>
        <button @onclick="@(() => ShowFileAsync("files/quote.pdf", 
                "General Ravon quote (PDF file)"))">
            Show PDF ('quote.pdf')
        </button>
    </div>
    <iframe id="iframe" style="height: calc(100vh - 200px)" />
</div>

@code
{
    private async Task<(Stream, string?)> DownloadFileAsync(string url)
    {
        var absoluteUrl = NavigationManager.ToAbsoluteUri(url);
        Console.WriteLine($"Downloading file from {absoluteUrl}");

        var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

        if (response.Content.Headers.TryGetValues("Content-Type", out var values))
        {
            contentType = values.FirstOrDefault();
        }

        return (await response.Content.ReadAsStreamAsync(), contentType);
    }

    private async Task ShowFileAsync(string url, string title)
    {
        var (fileStream, contentType) = await DownloadFileAsync(url);
        var strRef = new DotNetStreamReference(fileStream);
        await JS.InvokeVoidAsync("setSource", "iframe", strRef, contentType, title);
    }
}

その他のリソース