ASP.NET Core Blazor file downloads
Note
This isn't the latest version of this article. For the current release, see the .NET 8 version of this article.
This article explains how to download files in Blazor apps.
Throughout this article, the terms client/client-side and server/server-side are used to distinguish locations where app code executes:
- Client/client-side
- Client-side rendering (CSR) of a Blazor Web App. The
Program
file isProgram.cs
of the client project (.Client
). Blazor script start configuration is found in theApp
component (Components/App.razor
) of the server project. Routable Interactive WebAssembly and Interactive Auto render mode components with an@page
directive are placed in the client project'sPages
folder. Place non-routable shared components at the root of the.Client
project or in custom folders based on component functionality. - A Blazor WebAssembly app. The
Program
file isProgram.cs
. Blazor script start configuration is found in thewwwroot/index.html
file.
- Client-side rendering (CSR) of a Blazor Web App. The
- Server/server-side: Interactive server-side rendering (interactive SSR) of a Blazor Web App. The
Program
file isProgram.cs
of the server project. Blazor script start configuration is found in theApp
component (Components/App.razor
). Only routable Interactive Server render mode components with an@page
directive are placed in theComponents/Pages
folder. Non-routable shared components are placed in the server project'sComponents
folder. Create custom folders based on component functionality as needed.
- Client/client-side
- The
Client
project of a hosted Blazor WebAssembly app. - A Blazor WebAssembly app.
- Blazor script start configuration is found in the
wwwroot/index.html
file. - The
Program
file isProgram.cs
.
- The
- Server/server-side
- The
Server
project of a hosted Blazor WebAssembly app. - A Blazor Server app. Blazor script start configuration is found in
Pages/_Host.cshtml
. - The
Program
file isProgram.cs
.
- The
- Client/client-side
- The
Client
project of a hosted Blazor WebAssembly app. - A Blazor WebAssembly app.
- Blazor script start configuration is found in the
wwwroot/index.html
file. - The
Program
file isProgram.cs
.
- The
- Server/server-side
- The
Server
project of a hosted Blazor WebAssembly app. - A Blazor Server app. Blazor script start configuration is found in
Pages/_Layout.cshtml
. - The
Program
file isProgram.cs
.
- The
Files can be downloaded from the app's own static assets or from any other location:
- ASP.NET Core apps use Static File Middleware to serve files to clients of server-side apps.
- The guidance in this article also applies to other types of file servers that don't use .NET, such as Content Delivery Networks (CDNs).
This article covers approaches for the following scenarios:
- Stream file content to a raw binary data buffer on the client: Typically, this approach is used for relatively small files (< 250 MB).
- Download a file via a URL without streaming: Usually, this approach is used for relatively large files (> 250 MB).
When downloading files from a different origin than the app, Cross-Origin Resource Sharing (CORS) considerations apply. For more information, see the Cross-Origin Resource Sharing (CORS) section.
Security considerations
Use caution when providing users with the ability to download files from a server. Attackers may execute Denial of Service (DoS) attacks, API exploitation attacks, or attempt to compromise networks and servers in other ways.
Security steps that reduce the likelihood of a successful attack are:
- Download files from a dedicated file download area on the server, preferably from a non-system drive. Using a dedicated location makes it easier to impose security restrictions on downloadable files. Disable execute permissions on the file download area.
- Client-side security checks are easy to circumvent by malicious users. Always perform client-side security checks on the server, too.
- Don't receive files from users or other untrusted sources and then make the files available for immediate download without performing security checks on the files. For more information, see Upload files in ASP.NET Core.
Download from a stream
This section applies to files that are typically up to 250 MB in size.
The recommended approach for downloading relatively small files (< 250 MB) is to stream file content to a raw binary data buffer on the client with JavaScript (JS) interop.
Warning
The approach in this section reads the file's content into a JS ArrayBuffer
. This approach loads the entire file into the client's memory, which can impair performance. To download relatively large files (>= 250 MB), we recommend following the guidance in the Download from a URL section.
The following downloadFileFromStream
JS function:
- Reads the provided stream into an
ArrayBuffer
. - Creates a
Blob
to wrap theArrayBuffer
. - Creates an object URL to serve as the file's download address.
- Creates an
HTMLAnchorElement
(<a>
element). - Assigns the file's name (
fileName
) and URL (url
) for the download. - Triggers the download by firing a
click
event on the anchor element. - Removes the anchor element.
- Revokes the object URL (
url
) by callingURL.revokeObjectURL
. This is an important step to ensure memory isn't leaked on the client.
<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>
Note
For general guidance on JS location and our recommendations for production apps, see ASP.NET Core Blazor JavaScript interoperability (JS interop).
The following component:
- Uses native byte-streaming interop to ensure efficient transfer of the file to the client.
- Has a method named
GetFileStream
to retrieve a Stream for the file that's downloaded to clients. Alternative approaches include retrieving a file from storage or generating a file dynamically in C# code. For this demonstration, the app creates a 50 KB file of random data from a new byte array (new byte[]
). The bytes are wrapped with a MemoryStream to serve as the example's dynamically-generated binary file. - The
DownloadFileFromStream
method:- Retrieves the Stream from
GetFileStream
. - Specifies a file name when file is saved on the user's machine. The following example names the file
quote.txt
. - Wraps the Stream in a DotNetStreamReference, which allows streaming the file data to the client.
- Invokes the
downloadFileFromStream
JS function to accept the data on the client.
- Retrieves the Stream from
FileDownload1.razor
:
@page "/file-download-1"
@rendermode InteractiveServer
@using System.IO
@inject IJSRuntime JS
<h1>File Download Example 1</h1>
<button @onclick="DownloadFileFromStream">
Download File From Stream
</button>
@code {
private Stream GetFileStream()
{
var binaryData = new byte[50 * 1024];
var fileStream = new MemoryStream(binaryData);
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);
}
}
For a component in a server-side app that must return a Stream for a physical file, the component can call File.OpenRead, as the following example demonstrates:
private Stream GetFileStream()
{
return File.OpenRead(@"{PATH}");
}
In the preceding example, the {PATH}
placeholder is the path to the file. The @
prefix indicates that the string is a verbatim string literal, which permits the use of backslashes (\
) in a Windows OS path and embedded double-quotes (""
) for a single quote in the path. Alternatively, avoid the string literal (@
) and use either of the following approaches:
- Use escaped backslashes (
\\
) and quotes (\"
). - Use forward slashes (
/
) in the path, which are supported across platforms in ASP.NET Core apps, and escaped quotes (\"
).
Download from a URL
This section applies to files that are relatively large, typically 250 MB or larger.
The example in this section uses a download file named quote.txt
, which is placed in a folder named files
in the app's web root (wwwroot
folder). The use of the files
folder is only for demonstration purposes. You can organize downloadable files in any folder layout within the web root (wwwroot
folder) that you prefer, including serving the files directly from the wwwroot
folder.
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/)
The following triggerFileDownload
JS function:
- Creates an
HTMLAnchorElement
(<a>
element). - Assigns the file's name (
fileName
) and URL (url
) for the download. - Triggers the download by firing a
click
event on the anchor element. - Removes the anchor element.
<script>
window.triggerFileDownload = (fileName, url) => {
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = fileName ?? '';
anchorElement.click();
anchorElement.remove();
}
</script>
Note
For general guidance on JS location and our recommendations for production apps, see ASP.NET Core Blazor JavaScript interoperability (JS interop).
The following example component downloads the file from the same origin that the app uses. If the file download is attempted from a different origin, configure Cross-Origin Resource Sharing (CORS). For more information, see the Cross-Origin Resource Sharing (CORS) section.
Change the port in the following example to match the localhost development port of your environment.
FileDownload2.razor
:
@page "/file-download-2"
@rendermode InteractiveServer
@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 = Path.Combine("https://localhost:7029", "files", fileName);
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);
}
}
Cross-Origin Resource Sharing (CORS)
Without taking further steps to enable Cross-Origin Resource Sharing (CORS) for files that don't have the same origin as the app, downloading files won't pass CORS checks made by the browser.
For more information on CORS with ASP.NET Core apps and other Microsoft products and services that host files for download, see the following resources:
- Enable Cross-Origin Requests (CORS) in ASP.NET Core
- Using Azure CDN with CORS (Azure documentation)
- Cross-Origin Resource Sharing (CORS) support for Azure Storage (REST documentation)
- Core Cloud Services - Set up CORS for your website and storage assets (Learn module)
- IIS CORS module Configuration Reference (IIS documentation)
Additional resources
ASP.NET Core
Feedback
Submit and view feedback for