ASP.NET Core Blazor 檔案下載
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
本文說明如何在 Blazor 應用程式中下載檔案。
檔案下載
本文涵蓋了以下場景的方法,其中檔案不應由瀏覽器開啟,而是下載並儲存在用戶端上:
- 將檔案內容串流至用戶端上的原始二進位資料緩衝區:通常,此方法用於相對小型的檔案 (< 250 MB)。
- 透過 URL 下載檔案而不串流:通常,此方法用於相對大型的檔案 (> 250 MB)。
從與應用程式不同的來源下載檔案時,適用跨來源資源共用 (CORS) 考量。 如需詳細資訊,請參閱跨來源資源共用 (CORS) 一節。
安全性考量
為使用者提供從伺服器下載檔案的能力時,請特別注意。 網路攻擊者可能會執行拒絕服務 (DoS) 攻擊、API 惡意探索攻擊,或嘗試以其他方式入侵網路和伺服器。
降低成功攻擊可能性的安全性步驟如下:
- 從伺服器上的專用檔案下載區域下載檔案,最好是從非系統磁碟機進行。 使用專用位置可讓您更輕鬆地對可下載的檔案施加安全性限制。 停用檔案下載區域的執行權限。
- 用戶端安全性檢查很容易被惡意使用者規避。 而且一律在伺服器上執行用戶端安全性檢查。
- 請勿從使用者或其他不受信任的來源接收檔案,然後不對檔案執行安全性檢查,就讓檔案可供立即下載。 如需詳細資訊,請參閱在 ASP.NET Core 中上傳檔案。
從資料流下載
本節適用於大小通常高達 250 MB 的檔案。
下載相對小型檔案 (< 250 MB) 的建議方法是使用 JavaScript (JS) Interop 將檔案內容串流至用戶端上的原始二進位資料緩衝區。 這種方法對於採用互動式呈現模式的元件有效,但不適用於採用靜態伺服器端呈現的元件(靜態 SSR)。
下載相對小型檔案 (< 250 MB) 的建議方法是使用 JavaScript (JS) Interop 將檔案內容串流至用戶端上的原始二進位資料緩衝區。
警告
本節中的方法會將檔案的內容讀入 JS ArrayBuffer
中。 此方法會將整個檔案載入用戶端的記憶體中,這可能損害效能。 若要下載相對大型的檔案 (>= 250 MB),建議遵循從 URL 下載一節中的指引。
下列 downloadFileFromStream
JS 函式:
- 將提供的串流讀入
ArrayBuffer
。 - 建立
Blob
以包裝ArrayBuffer
。 - 建立物件 URL 作為檔案的下載位址。
- 建立
HTMLAnchorElement
(<a>
元素)。 - 指派可供下載的檔案名稱 (
fileName
) 和 URL (url
)。 - 在錨點元素上引發
click
事件,以觸發下載。 - 移除錨點元素。
- 呼叫
URL.revokeObjectURL
以撤銷物件 URL (url
)。 這是用以確保用戶端不會流失記憶體的重要步驟。
<script>
window.downloadFileFromStream = async (fileName, contentStreamReference) => {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = fileName ?? '';
anchorElement.click();
anchorElement.remove();
URL.revokeObjectURL(url);
}
</script>
注意
如需 JS 位置的一般指導和我們對於生產應用程式的建議,請參閱 ASP.NET Core Blazor 應用程式中的 JavaScript 位置
下列元件:
- 使用原生位元組串流 Interop 來確保將檔案有效率地傳輸至用戶端。
- 具有名為
GetFileStream
的方法,可針對下載至用戶端的檔案擷取 Stream。 替代方法包括從儲存體擷取檔案,或在 C# 程式碼中動態產生檔案。 在此示範中,應用程式會建立 50 KB 的檔案,內含來自新位元組陣列 (new byte[]
) 的隨機資料。 位元組會包裝成 MemoryStream,作為範例動態產生的二進位檔案。 DownloadFileFromStream
方法:- 從
GetFileStream
擷取 Stream。 - 指定在使用者電腦上儲存檔案時的檔案名稱。 下列範例會將檔案命名為
quote.txt
。 - 將 Stream 包裝在 DotNetStreamReference 中,以允許將檔案資料串流至用戶端。
- 叫用
downloadFileFromStream
JS 函式以接受用戶端上的資料。
- 從
FileDownload1.razor
:
@page "/file-download-1"
@using System.IO
@inject IJSRuntime JS
<PageTitle>File Download 1</PageTitle>
<h1>File Download Example 1</h1>
<button @onclick="DownloadFileFromStream">
Download File From Stream
</button>
@code {
private Stream GetFileStream()
{
var randomBinaryData = new byte[50 * 1024];
var fileStream = new MemoryStream(randomBinaryData);
return fileStream;
}
private async Task DownloadFileFromStream()
{
var fileStream = GetFileStream();
var fileName = "log.bin";
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
}
@page "/file-download-1"
@using System.IO
@inject IJSRuntime JS
<PageTitle>File Download 1</PageTitle>
<h1>File Download Example 1</h1>
<button @onclick="DownloadFileFromStream">
Download File From Stream
</button>
@code {
private Stream GetFileStream()
{
var randomBinaryData = new byte[50 * 1024];
var fileStream = new MemoryStream(randomBinaryData);
return fileStream;
}
private async Task DownloadFileFromStream()
{
var fileStream = GetFileStream();
var fileName = "log.bin";
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
}
@page "/file-download-1"
@using System.IO
@inject IJSRuntime JS
<h1>File Download Example</h1>
<button @onclick="DownloadFileFromStream">
Download File From Stream
</button>
@code {
private Stream GetFileStream()
{
var randomBinaryData = new byte[50 * 1024];
var fileStream = new MemoryStream(randomBinaryData);
return fileStream;
}
private async Task DownloadFileFromStream()
{
var fileStream = GetFileStream();
var fileName = "log.bin";
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
}
@page "/file-download-1"
@using System.IO
@inject IJSRuntime JS
<h1>File Download Example</h1>
<button @onclick="DownloadFileFromStream">
Download File From Stream
</button>
@code {
private Stream GetFileStream()
{
var randomBinaryData = new byte[50 * 1024];
var fileStream = new MemoryStream(randomBinaryData);
return fileStream;
}
private async Task DownloadFileFromStream()
{
var fileStream = GetFileStream();
var fileName = "log.bin";
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
}
對於伺服器端應用程式中必須針對實體檔案傳回 Stream 的元件,元件可以呼叫 File.OpenRead,如下列範例所示:
private Stream GetFileStream() => File.OpenRead(@"{PATH}");
在上述範例中,{PATH}
預留位置是檔案的路徑。 @
前置詞表示字串是逐字字串常值,允許在 Windows OS 路徑中使用反斜線 (\
),以及在路徑中針對單引號使用內嵌雙引號 (""
)。 或者,避免使用字串常值 (@
),而使用下列其中一種方法:
- 使用逸出的反斜線 (
\\
) 和引號 (\"
)。 - 在路徑中使用正斜線 (
/
),其在 ASP.NET Core 應用程式中跨平台支援並逸出引號 (\"
)。
從 URL 下載
本節適用於相對大型的檔案,通常為 250 MB 或更大。
下載以互動式呈現元件之相對大型檔案(>= 250 MB)、或統計呈現元件之任何大小檔案的建議方法是使用 JS 來觸發具有檔名和 URL 的錨點元素。
下載相對大型檔案(>= 250 MB)的建議方法是使用 JS 來觸發具有檔名和 URL 的錨點元素。
本節中的範例使用名為 quote.txt
的下載檔案,該檔案放在應用程式的 Web 根目錄中名為 files
的資料夾 (wwwroot
資料夾)。 files
資料夾的使用僅供示範之用。 您可以在您偏好的 Web 根目錄 (wwwroot
資料夾) 內的任何資料夾配置中組織可下載的檔案,包括直接從 wwwroot
資料夾提供檔案。
wwwroot/files/quote.txt
:
When victory is ours, we'll wipe every trace of the Thals and their city from the face of this land. We will avenge the deaths of all Kaleds who've fallen in the cause of right and justice and build a peace which will be a monument to their sacrifice. Our battle cry will be "Total extermination of the Thals!"
- General Ravon (Guy Siner, http://guysiner.com/)
Dr. Who: Genesis of the Daleks (https://www.bbc.co.uk/programmes/p00vd5g2)
Copyright 1975 BBC (https://www.bbc.co.uk/)
When victory is ours, we'll wipe every trace of the Thals and their city from the face of this land. We will avenge the deaths of all Kaleds who've fallen in the cause of right and justice and build a peace which will be a monument to their sacrifice. Our battle cry will be "Total extermination of the Thals!"
- General Ravon (Guy Siner, http://guysiner.com/)
Dr. Who: Genesis of the Daleks (https://www.bbc.co.uk/programmes/p00vd5g2)
Copyright 1975 BBC (https://www.bbc.co.uk/)
When victory is ours, we'll wipe every trace of the Thals and their city from the face of this land. We will avenge the deaths of all Kaleds who've fallen in the cause of right and justice and build a peace which will be a monument to their sacrifice. Our battle cry will be "Total extermination of the Thals!"
- General Ravon (Guy Siner, http://guysiner.com/)
Dr. Who: Genesis of the Daleks (https://www.bbc.co.uk/programmes/p00vd5g2)
Copyright 1975 BBC (https://www.bbc.co.uk/)
When victory is ours, we'll wipe every trace of the Thals and their city from the face of this land. We will avenge the deaths of all Kaleds who've fallen in the cause of right and justice and build a peace which will be a monument to their sacrifice. Our battle cry will be "Total extermination of the Thals!"
- General Ravon (Guy Siner, http://guysiner.com/)
Dr. Who: Genesis of the Daleks (https://www.bbc.co.uk/programmes/p00vd5g2)
Copyright 1975 BBC (https://www.bbc.co.uk/)
下列 triggerFileDownload
JS 函式:
- 建立
HTMLAnchorElement
(<a>
元素)。 - 指派可供下載的檔案名稱 (
fileName
) 和 URL (url
)。 - 在錨點元素上引發
click
事件,以觸發下載。 - 移除錨點元素。
<script>
window.triggerFileDownload = (fileName, url) => {
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = fileName ?? '';
anchorElement.click();
anchorElement.remove();
}
</script>
注意
如需 JS 位置的一般指導和我們對於生產應用程式的建議,請參閱 ASP.NET Core 中的 JavaScript 位置Blazor 應用程式。
下列範例元件從應用程式使用的相同來源下載檔案。 如果嘗試從不同的來源下載檔案,請設定跨來源資源共用 (CORS)。 如需詳細資訊,請參閱跨來源資源共用 (CORS) 一節。
FileDownload2.razor
:
@page "/file-download-2"
@inject IJSRuntime JS
<PageTitle>File Download 2</PageTitle>
<h1>File Download Example 2</h1>
<button @onclick="DownloadFileFromURL">
Download File From URL
</button>
@code {
private async Task DownloadFileFromURL()
{
var fileName = "quote.txt";
var fileURL = "/files/quote.txt";
await JS.InvokeVoidAsync("triggerFileDownload", fileName, fileURL);
}
}
針對互動式元件,下列範例中的按鈕會呼叫 DownloadFileFromURL
處理常式來呼叫執行 JavaScript (JS) 函數 triggerFileDownload
:
如果元件採用靜態伺服器端轉譯 (靜態 SSR),請新增一個事件處理程式,供按鈕 (addEventListener
(MDN 文件)) 呼叫 triggerFileDownload
遵循 ASP.NET CoreBlazor JavaScript 使用靜態伺服器端轉譯 (靜態 SSR)之指引。
@page "/file-download-2"
@inject IJSRuntime JS
<PageTitle>File Download 2</PageTitle>
<h1>File Download Example 2</h1>
<button @onclick="DownloadFileFromURL">
Download File From URL
</button>
@code {
private async Task DownloadFileFromURL()
{
var fileName = "quote.txt";
var fileURL = "/files/quote.txt";
await JS.InvokeVoidAsync("triggerFileDownload", fileName, fileURL);
}
}
針對互動式元件,下列範例中的按鈕會呼叫 DownloadFileFromURL
處理常式來呼叫執行 JavaScript (JS) 函數 triggerFileDownload
:
如果元件採用靜態伺服器端轉譯 (靜態 SSR),請新增一個事件處理程式,供按鈕 (addEventListener
(MDN 文件)) 呼叫 triggerFileDownload
遵循 ASP.NET CoreBlazor JavaScript 使用靜態伺服器端轉譯 (靜態 SSR)之指引。
@page "/file-download-2"
@inject IJSRuntime JS
<h1>File Download Example 2</h1>
<button @onclick="DownloadFileFromURL">
Download File From URL
</button>
@code {
private async Task DownloadFileFromURL()
{
var fileName = "quote.txt";
var fileURL = "https://localhost:5001/files/quote.txt";
await JS.InvokeVoidAsync("triggerFileDownload", fileName, fileURL);
}
}
變更下列範例中的連接埠,以便和所處環境的 localhost 開發連接埠做比對。
@page "/file-download-2"
@inject IJSRuntime JS
<h1>File Download Example 2</h1>
<button @onclick="DownloadFileFromURL">
Download File From URL
</button>
@code {
private async Task DownloadFileFromURL()
{
var fileName = "quote.txt";
var fileURL = "https://localhost:5001/files/quote.txt";
await JS.InvokeVoidAsync("triggerFileDownload", fileName, fileURL);
}
}
變更下列範例中的連接埠,以便和所處環境的 localhost 開發連接埠做比對。
跨來源資源共用 (CORS)
若未採取進一步步驟來為沒有與應用程式相同來源的檔案啟用跨來源資源共用 (CORS),下載檔案將不會通過瀏覽器所進行的 CORS 檢查。
如需搭配 ASP.NET Core 應用程式及裝載檔案以供下載的其他 Microsoft 產品和服務的 CORS 詳細資訊,請參閱下列資源:
- 在 ASP Core 中啟用跨原始來源要求 (CORS)
- 使用 Azure CDN 搭配 CORS (Azure 文件)
- Azure 儲存體的跨來源資源共用 (CORS) 支援 (REST 文件)
- 核心雲端服務 - 為您的網站和儲存體資產設定 CORS (Learn 模組)
- IIS CORS 模組組態參考 (IIS 文件)