Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions aspnetcore/blazor/file-downloads.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ The following `DownloadFileFromStream` method performs the following steps:
}
```

The JavaScript `downloadFileFromStream` function accepts the file name and data stream and triggers the client-side download. The function performs the following steps:
The JavaScript `downloadFileFromStream` function accepts the file name with the data stream and triggers the client-side download. The function performs the following steps:

* Read the provided stream into an [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer).
* Create a [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob) to wrap the `ArrayBuffer`.
* Create an object URL to serve as the address for the file to be downloaded.
* Create an object URL to serve as the file's download address.

The `fileName` and object `url` are passed to `triggerFileDownload`, which performs the following steps:

Expand All @@ -84,14 +84,12 @@ The `fileName` and object `url` are passed to `triggerFileDownload`, which perfo
* Trigger the download via `click`.
* Remove the anchor (`<a>`) element.

At this point, the file download is triggered. It should be safe to revoke the temporary object URL by calling [`revokeObjectURL`](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL) on the URL. **This is an important step to ensure memory isn't leaked on the client.**

At this point, the file download is triggered and then the temporary object URL is revoked by calling [`revokeObjectURL`](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL) on the URL. **This is an important step to ensure memory isn't leaked on the client.**

```javascript
async function downloadFileFromStream(fileName, contentStreamReference) {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);

const url = URL.createObjectURL(blob);

triggerFileDownload(fileName, url);
Expand All @@ -102,23 +100,41 @@ async function downloadFileFromStream(fileName, contentStreamReference) {
function triggerFileDownload(fileName, url) {
const anchorElement = document.createElement('a');
anchorElement.href = url;

if (fileName) {
anchorElement.download = fileName;
}

anchorElement.download = fileName ?? '';
anchorElement.click();
anchorElement.remove();
}
```

In the preceding example, the call to `contentStreamReference.arrayBuffer` loads the entire file into client memory. For file downloads over 250 MB, we recommend downloading the file from a URL instead:

```razor
<button @onclick="DownloadFileFromURL">
Download File From URL
</button>

@code {
private async Task DownloadFileFromURL()
{
var fileURL = "{FILE URL}";
var fileName = "{FILE NAME}";
await JS.InvokeVoidAsync("triggerFileDownload", fileName, fileURL);
}
}
```

In the preceding example, replace the placeholders with the following values:

* `{FILE URL}`: The URL of the file to download. Example: `https://www.contoso.com/files/log0001.txt`
* `{FILE NAME}`: The file name to use for the saved file. Example: `log-0001.txt`

## File streams

In Blazor WebAssembly apps, file data is streamed directly from .NET code into the browser. In Blazor Server apps, file data is streamed over the SignalR connection from .NET code into the browser.

## Security considerations

Use caution when providing users with the ability to download files from a server. Attackers may execute [denial of service (DOS)](/windows-hardware/drivers/ifs/denial-of-service) attacks or attempt to compromise networks and servers in other ways.
Use caution when providing users with the ability to download files from a server. Attackers may execute [denial of service (DOS)](/windows-hardware/drivers/ifs/denial-of-service) attacks, [API exploitation attacks](https://developer.mozilla.org/docs/Web/HTML/Element/a#security_and_privacy), or attempt to compromise networks and servers in other ways.

Security steps that reduce the likelihood of a successful attack are:

Expand All @@ -127,6 +143,7 @@ Security steps that reduce the likelihood of a successful attack are:

## Additional resources

* [`<a>`: The Anchor element: Security and privacy (MDN documentation)](https://developer.mozilla.org/docs/Web/HTML/Element/a#security_and_privacy)
* <xref:blazor/file-uploads>
* <xref:mvc/models/file-uploads#security-considerations>
* <xref:blazor/forms-validation>
2 changes: 1 addition & 1 deletion aspnetcore/tutorials/razor-pages/model.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ In this section, the **Package Manager Console** (PMC) window is used to:

---

The preceding commands install [the Entity Framework Core tools](https://docs.microsoft.com/ef/core/get-started/overview/install#get-the-entity-framework-core-tools) and run the `migrations` command to generate code that creates the initial database schema.
The preceding commands install [the Entity Framework Core tools](/ef/core/get-started/overview/install#get-the-entity-framework-core-tools) and run the `migrations` command to generate code that creates the initial database schema.

The following warning is displayed, which is addressed in a later step:

Expand Down