ASP.NET Core Blazor 檔案下載

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱 本文的 ASP.NET Core 8.0 版本。

本文說明如何在 Blazor 應用程式中下載檔案。

檔案下載

您可從應用程式自己的靜態資產,或從任何其他位置下載檔案:

  • ASP.NET Core 應用程式會使用靜態檔案中介軟體,將檔案提供給伺服器端應用程式的用戶端。
  • 本文中的指引也適用於不使用 .NET 的其他檔案伺服器類型,例如內容傳遞網路 (CDN)。

本文涵蓋下列案例的方法:

從與應用程式不同的來源下載檔案時,適用跨來源資源共用 (CORS) 考量。 如需詳細資訊,請參閱跨來源資源共用 (CORS) 一節。

安全性考量

為使用者提供從伺服器下載檔案的能力時,請特別注意。 攻擊者可能會以其他方式執行 阻斷服務 (DoS) 攻擊、 API 惡意探索攻擊,或嘗試入侵網路和伺服器。

降低成功攻擊可能性的安全性步驟如下:

  • 從伺服器上的專用檔案下載區域下載檔案,最好是從非系統磁碟機進行。 使用專用位置可讓您更輕鬆地對可下載的檔案施加安全性限制。 停用檔案下載區域的執行權限。
  • 用戶端安全性檢查很容易被惡意使用者規避。 而且一律在伺服器上執行用戶端安全性檢查。
  • 請勿從使用者或其他不受信任的來源接收檔案,然後不對檔案執行安全性檢查,就讓檔案可供立即下載。 如需詳細資訊,請參閱在 ASP.NET Core 中上傳檔案

從資料流下載

本節適用於大小通常高達 250 MB 的檔案。

下載相對小型檔案 (< 250 MB) 的建議方法是使用 JavaScript (JS) Interop 將檔案內容串流至用戶端上的原始二進位資料緩衝區。

警告

本節中的方法會將檔案的內容讀入 JS ArrayBuffer 中。 此方法會將整個檔案載入用戶端的記憶體中,這可能損害效能。 若要下載相對大型的檔案 (>= 250 MB),建議遵循從 URL 下載一節中的指引。

下列 downloadFileFromStreamJS 函式:

  • 將提供的串流讀入 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 互通性 (JS interop)

下列元件:

  • 使用原生位元組串流 Interop 來確保將檔案有效率地傳輸至用戶端。
  • 具有名為 GetFileStream 的方法,可針對下載至用戶端的檔案擷取 Stream。 替代方法包括從儲存體擷取檔案,或在 C# 程式碼中動態產生檔案。 在此示範中,應用程式會建立 50 KB 的檔案,內含來自新位元組陣列 (new byte[]) 的隨機資料。 位元組會包裝成 MemoryStream,作為範例動態產生的二進位檔案。
  • DownloadFileFromStream 方法:
    • GetFileStream 擷取 Stream
    • 指定在使用者電腦上儲存檔案時的檔案名稱。 下列範例會將檔案命名為 quote.txt
    • Stream 包裝在 DotNetStreamReference 中,以允許將檔案資料串流至用戶端。
    • 叫用 downloadFileFromStreamJS 函式以接受用戶端上的資料。

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

<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()
{
    return File.OpenRead(@"{PATH}");
}

在上述範例中,{PATH} 預留位置是檔案的路徑。 @ 前置詞表示字串是逐字字串常值,允許在 Windows OS 路徑中使用反斜線 (\),以及在路徑中針對單引號使用內嵌雙引號 ("")。 或者,避免使用字串常值 (@),而使用下列其中一種方法:

  • 使用逸出的反斜線 (\\) 和引號 (\")。
  • 在路徑中使用正斜線 (/),其在 ASP.NET Core 應用程式中跨平台支援並逸出引號 (\")。

從 URL 下載

本節適用於相對大型的檔案,通常為 250 MB 或更大。

本節中的範例使用名為 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/p00pq2gc)
  ©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/p00pq2gc)
  ©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/p00pq2gc)
  ©1975 BBC (https://www.bbc.co.uk/)

下列 triggerFileDownloadJS 函式:

  • 建立 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 Blazor JavaScript 互通性 (JS interop)

下列範例元件從應用程式使用的相同來源下載檔案。 如果嘗試從不同的來源下載檔案,請設定跨來源資源共用 (CORS)。 如需詳細資訊,請參閱跨來源資源共用 (CORS) 一節。

變更下列範例中的連接埠,以符合您環境的 localhost 開發連接埠。

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);
    }
}
@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);
    }
}
@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);
    }
}

跨來源資源共用 (CORS)

若未採取進一步步驟來為沒有與應用程式相同來源的檔案啟用跨來源資源共用 (CORS),下載檔案將不會通過瀏覽器所進行的 CORS 檢查。

如需搭配 ASP.NET Core 應用程式及裝載檔案以供下載的其他 Microsoft 產品和服務的 CORS 詳細資訊,請參閱下列資源:

其他資源