在 ASP.NET Core 顯示影像和文件 Blazor
注意
這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
本文說明在 Blazor 應用程式顯示影像和文件的方法。
本文範例可透過 Blazor 範例應用程式進行檢查和使用:
dotnet/blazor-samples
GitHub 存放庫:瀏覽至名為 BlazorSample_BlazorWebApp
(8.0 或更新版本)、BlazorSample_Server
(7.0 或更早版本) 或 BlazorSample_WebAssembly
的應用程式。
動態設定映像來源
下列範例示範如何使用 C# 欄位,動態設定映像的來源。
本節範例使用三個影像檔,名為 image1.png
、 image2.png
和 image3.png
。 將影像放在應用程式 Web 根目錄 (wwwroot
) 名為 images
的資料夾。 images
資料夾的使用僅供示範之用。 您能夠以您任何偏好的資料夾配置整理影像,包括直接從 wwwroot
資料夾提供影像。
在下列 ShowImage1
元件中:
- 映像的來源 (
src
) 會以動態方式設定為 C# 中imageSource
的值。 ShowImage
方法會根據傳遞至方法的映像id
引數來更新imageSource
欄位。- 轉譯的按鈕會針對
images
資料夾中三個可用的映像,使用映像引數呼叫ShowImage
方法。 檔案名稱是使用傳遞至方法的引數所組成,並符合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 運算式中使用迴圈變數,例如上述 for
迴圈範例中的 i
。 否則,所有 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 的串流 Interop 功能,直接將影像傳送至用戶端,而不用將影像裝載到公開 URL。
本節範例會使用 JavaScript (JS) Interop 對來源資料進行串流。 下列 setSource
JS 函式:
- 可用於串流下列元素:
<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
C# 方法以擷取映像的 Stream。 生產應用程式可能會根據特定使用者動態產生映像,或從儲存體擷取映像。 下列範例會擷取dotnet
GitHub 存放庫的 .NET 虛擬人偶。 - 具有在使用者選取之按鈕上觸發的
SetImageAsync
方法。SetImageAsync
會執行下列步驟:- 從
GetImageStreamAsync
擷取 Stream。 - 將 Stream 包裝在 DotNetStreamReference 中,以允許將映像資料串流至用戶端。
- 叫用
setSource
JavaScript 函式,此函式會接受用戶端上的資料。
- 從
注意
伺服器端應用程式會使用專用 HttpClient 服務來提出要求,因此伺服器端 Blazor 應用程式的開發人員不需要採取任何動作,即可註冊 HttpClient 服務。 從 Blazor 專案範本建立應用程式時,用戶端應用程式會有預設的 HttpClient 服務註冊。 如果用戶端應用程式的 Program
檔案中不存在 HttpClient 服務註冊,請透過新增 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>
(MDN 文件)。
警告
使用下列範例的 <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<(Stream, string?)> DownloadFileAsync(string url)
{
var absoluteUrl = Navigation.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 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<(Stream, string?)> DownloadFileAsync(string url)
{
var absoluteUrl = Navigation.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);
}
}