次の方法で共有


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

Note

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

警告

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

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

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

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

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

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

このセクションの例では、image1.pngimage2.png、および image3.png という名前の 3 つのイメージ ファイルを使用します。 アプリの Web ルート (images) の wwwroot という名前の新しいフォルダーに、画像を配置します。 images フォルダーは、デモンストレーションのためにのみ使います。 wwwroot フォルダーから直接資産を提供するなど、任意のフォルダー レイアウトに静的な資産を整理できます。

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

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

<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# のプロパティを使用してデータを保持することもできます。

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

  • ループ変数 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 のコンテンツ タイプを設定して、Blob をラップするArrayBufferを作成します。
  • 表示するドキュメントのアドレスとなるオブジェクト 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> タグを含みます。
  • 画像の GetImageStreamAsync を取得する Stream C# メソッドがあります。 運用アプリでは、特定のユーザーに基づいて画像を動的に生成したり、ストレージから画像を取得したりできます。 次の例では、dotnet GitHub リポジトリの .NET アバターを取得します。
  • ユーザーがボタンを選ぶとトリガーされる SetImageAsync メソッドがあります。 SetImageAsync では次の手順が実行されます。
    • Stream から GetImageStreamAsync を取得します。
    • 画像データをクライアントにストリーミングできるように、StreamDotNetStreamReference でラップします。
    • クライアント上のデータを受け取る setSource JavaScript 関数を呼び出します。

Note

サーバー側アプリは専用の HttpClient サービスを使って要求を行うので、開発者は Blazor サービスを登録するためにサーバー側 HttpClient アプリで何も行う必要はありません。 クライアント側アプリには、HttpClient プロジェクト テンプレートからアプリが作成されるときに、既定の Blazor サービス登録があります。 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() => 
        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

<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() => 
        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> 要素に読み込みます。

警告

次の例の <iframe> 要素の使用は安全であり、コンテンツは信頼できるソースであるアプリから読み込まれるため、サンドボックスは必要ありません。

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

ShowFile.razor:

@page "/show-file"
@inject NavigationManager Navigation
@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 ShowFileAsync(string url, string title)
    {
        var absoluteUrl = Navigation.ToAbsoluteUri(url);
        using var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

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

        var fileStream = await response.Content.ReadAsStreamAsync();
        var strRef = new DotNetStreamReference(fileStream);

        await JS.InvokeVoidAsync("setSource", "iframe", strRef, contentType, title);
    }
}
@page "/show-file"
@inject NavigationManager Navigation
@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 ShowFileAsync(string url, string title)
    {
        var absoluteUrl = Navigation.ToAbsoluteUri(url);
        using var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

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

        var fileStream = await response.Content.ReadAsStreamAsync();
        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 ShowFileAsync(string url, string title)
    {
        var absoluteUrl = NavigationManager.ToAbsoluteUri(url);
        using var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

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

        var fileStream = await response.Content.ReadAsStreamAsync();
        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 ShowFileAsync(string url, string title)
    {
        var absoluteUrl = NavigationManager.ToAbsoluteUri(url);
        using var response = await Http.GetAsync(absoluteUrl);
        string? contentType = null;

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

        var fileStream = await response.Content.ReadAsStreamAsync();
        var strRef = new DotNetStreamReference(fileStream);

        await JS.InvokeVoidAsync("setSource", "iframe", strRef, contentType, title);
    }
}

前の例では、using変数のresponse ステートメントは、HttpResponseMessageのスコープが終了するまで、ShowFileAsync インスタンスを破棄しません。 オープン ストリームは、JavaScript 相互運用機能を介してファイル データを setSource 関数に転送するのに十分な長さに維持されます。 HttpResponseMessage インスタンスの破棄の重要性に関する一般的なガイダンスについては、「ASP.NET Core Blazor アプリから Web API を呼び出す」を参照してください。

その他のリソース