注意
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅本文的 .NET 9 版本。
本文介绍在 Blazor 应用中显示图像和文档的方法。
本文中的示例可在 Blazor 示例应用中查看和使用:
              
              dotnet/blazor-samples GitHub 存储库:导航到名为 BlazorSample_BlazorWebApp(8.0 或更高版本)、BlazorSample_Server(7.0 或更早版本)或 BlazorSample_WebAssembly 的应用。
动态设置图像源
下面的示例演示如何使用 C# 字段动态设置图像的源。
本节中的示例使用三个图像文件,分别名为 image1.png、image2.png 和image3.png。 图像放置在应用的 Web 根目录 (images) 中名为 wwwroot 的文件夹中。 
              images 文件夹的使用仅用于演示目的。 可以按照你喜欢的任何文件夹布局来组织图像资产,包括直接从 wwwroot 文件夹提供图像。
在下面的 ShowImage1 组件中:
- 图像中的源 (
src) 动态设置为 C# 中的值imageSource。 - 
              
ShowImage方法基于传递到方法的图像imageSource参数更新id字段。 - 呈现的按钮使用 
ShowImage文件夹中每个可用图像(共三个)的图像参数调用images方法。 文件名使用传递给该方法的参数组成,并匹配images文件夹中的三个图像之一。 
              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# 属性来保存数据。
避免直接在 Lambda 表达式中使用循环变量,如前面的 i 循环示例中的 for。 否则,所有 Lambda 表达式将使用相同的变量,这将导致在所有 Lambda 中使用相同的值。 在局部变量中捕获该变量的值。 在上面的示例中:
- 将循环变量 
i分配到imageId。 - 将 
imageId用于 lambda 表达式。 
或者,将 foreach 循环与 Enumerable.Range 结合使用,这样就能避开上述问题:
@foreach (var imageId in Enumerable.Range(1, 3))
{
    <button @onclick="() => ShowImage(imageId)">
        Image @imageId
    </button>
}
有关 lambda 表达式和事件处理的详细信息,请参阅 ASP.NET Core Blazor 事件处理。
流式传输图像或文档数据
可以使用 Blazor 的流式互操作功能将图像或其他文档类型(例如 PDF)直接传输到客户端,而不是将文件托管在公共 URL 上。
本部分中的示例使用 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>
注意
有关 JS 的常规指导和我们对常规应用的建议,请参阅 ASP.NET Core Blazor 应用中的 JavaScript 位置。
以下 ShowImage2 组件:
- 为 System.Net.Http.HttpClient 和 Microsoft.JSInterop.IJSRuntime 注入服务。
 - 包含用于显示图像的标记 
<img>。 - 拥有用于检索图像的 
GetImageStreamAsync的 Stream C# 方法。 生产应用可以基于特定用户动态生成图像,或者从存储中检索图像。 以下示例检索dotnetGitHub 存储库的 .NET 头像。 - 拥有在用户选择按钮时触发的 
SetImageAsync方法。SetImageAsync执行下列步骤:- 从 Stream 检索 
GetImageStreamAsync。 - 将 Stream 包装在 DotNetStreamReference 中,这允许将图像数据流式传输到客户端。
 - 调用 
setSourceJavaScript 函数,该函数接受客户端上的数据。 
 - 从 Stream 检索 
 
注意
服务器端应用使用专用的 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);
    }
}
在前面的示例中,直到ShowFileAsync的作用域结束,using变量对应的response语句不会对HttpResponseMessage实例进行处理。 为了将文件数据通过 JavaScript 互操作传输到 setSource 函数,打开的流保持足够长的时间。 有关处理 HttpResponseMessage 实例重要性的一般指南,请参阅 从 ASP.NET Core Blazor 应用调用 Web API。