ASP.NET Core Blazor-Dateidownloads

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

In diesem Artikel wird erläutert, wie Sie Dateien in Blazor-Apps herunterladen.

Dateidownloads

Dateien können aus den eigenen statischen Ressourcen der App oder von einem beliebigen anderen Speicherort heruntergeladen werden:

  • ASP.NET Core-Apps verwenden Middleware für statische Dateien, um Dateien an Clients von serverseitigen Apps zu übertragen.
  • Die Anleitung in diesem Artikel gilt auch für andere Arten von Dateiservern, die .NET nicht verwenden, z. B. Content Delivery Networks (CDNs).

In diesem Artikel werden Ansätze für die folgenden Szenarien behandelt, in denen eine Datei nicht von einem Browser geöffnet, sondern auf den Client heruntergeladen und gespeichert werden sollte:

Beim Herunterladen von Dateien aus einem anderen Ursprung als dem der App sind Überlegungen zu CORS (Cross-Origin Resource Sharing, Ressourcenfreigabe zwischen verschiedenen Ursprüngen) anzustellen. Weitere Informationen finden Sie im Abschnitt Cross-Origin Resource Sharing (CORS).

Sicherheitsüberlegungen

Gehen Sie mit Bedacht vor, wenn Sie Benutzern die Möglichkeit geben, Dateien von einem Server herunterzuladen. Angreifer*innen können Denial-of-Service-Angriffe (DoS) oder Angriffe zur Ausnutzung der API ausführen oder versuchen, Netzwerke und Server auf andere Weise zu kompromittieren.

Folgende Schritte können Sie dabei unterstützen, die Wahrscheinlichkeit eines erfolgreichen Angriffs zu verringern:

  • Laden Sie Dateien aus einem dedizierten Dateidownloadbereich auf dem Server herunter, vorzugsweise nicht vom Systemlaufwerk. Die Verwendung eines dedizierten Speicherorts macht es einfacher, Sicherheitsbeschränkungen für heruntergeladene Dateien zu erzwingen. Deaktivieren Sie Ausführungsberechtigungen für den Dateidownloadbereich.
  • Clientseitige Sicherheitsüberprüfungen können von böswilligen Benutzern leicht umgangen werden. Führen Sie clientseitige Sicherheitsüberprüfungen auch immer auf dem Server aus.
  • Empfangen Sie keine Dateien von Benutzern oder anderen nicht vertrauenswürdigen Quellen, und stellen Sie die Dateien nicht zum sofortigen Download zur Verfügung, ohne Sicherheitsüberprüfungen für die Dateien auszuführen. Weitere Informationen finden Sie unter Hochladen von Dateien in ASP.NET Core.

Herunterladen aus einem Stream

Dieser Abschnitt gilt für Dateien, die in der Regel bis zu 250 MB groß sind.

Der empfohlene Ansatz zum Herunterladen relativ kleiner Dateien (< 250 MB) ist das Streamen von Dateiinhalten in einen Datenpuffer für unformatierte Binärdaten auf dem Client mit JavaScript-Interoperabilität (JS).

Warnung

Bei dem Ansatz in diesem Abschnitt wird der Inhalt der Datei in ein JS ArrayBuffer eingelesen. Dieser Ansatz lädt die gesamte Datei in den Arbeitsspeicher des Clients, was die Leistung beeinträchtigen kann. Zum Herunterladen relativ großer Dateien (>= 250 MB) empfehlen wir, die Anleitung im Abschnitt Herunterladen über eine URL zu befolgen.

Die folgende JS-Funktion downloadFileFromStream:

  • Sie liest den bereitgestellten Datenstrom in einen ArrayBuffer ein.
  • Sie erstellt einen Blob zum Umschließen des ArrayBuffer.
  • Erstellen einer Objekt-URL, die als Adresse für die herunterzuladende Datei dient
  • Erstellen von HTMLAnchorElement (<a>-Element)
  • Erstellen des Dateinamens (fileName) und der URL (url) für den Download
  • Auslösen des Downloads durch Auslösen eines click-Ereignisses für das Ankerelement
  • Entfernen des Ankerelements
  • Widerrufen der Objekt-URL (url) durch Aufrufen von URL.revokeObjectURL. Dies ist ein wichtiger Schritt, um sicherzustellen, dass es auf dem Client nicht zu einem Speicherleck kommt.
<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>

Hinweis

Allgemeine Anleitungen zum JSSpeicherort und zu unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET CoreBlazor-Apps in ASP.NET Core.

Die folgende -Komponente:

  • Sie verwendet native Bytestreaminginteroperabilität, um die effiziente Übertragung der Datei an den Client zu gewährleisten.
  • Sie beinhaltet eine Methode namens GetFileStream zum Abrufen einer Stream-Klasse für die Datei, die auf Clients heruntergeladen wird. Alternative Ansätze umfassen das Abrufen einer Datei aus dem Speicher oder das dynamische Generieren einer Datei im C#-Code. Für diese Demonstration erstellt die App eine 50-KB-Datei mit zufälligen Daten aus einem neuen Bytearray (new byte[]). Die Bytes werden mit MemoryStream umschlossen, um als dynamisch generierte Binärdatei für das Beispiel zu dienen:
  • Die DownloadFileFromStream-Methode:
    • Die Methode ruft den Stream aus GetFileStream ab.
    • Gibt einen Dateinamen an, wenn die Datei auf dem Computer des Benutzers oder der Benutzerin gespeichert wird. Im folgenden Beispiel erhält die Datei den Namen quote.txt.
    • Stream wird von DotNetStreamReference umschlossen, was das Streamen der Dateidaten an den Client ermöglicht.
    • Ruft die JS-Funktion downloadFileFromStream auf, um die Daten auf dem Client zu akzeptieren.

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

Für eine Komponente in einer serverseitigen App, die Stream für eine physische Datei zurückgeben muss, kann die Komponente File.OpenRead aufrufen, wie das folgende Beispiel veranschaulicht:

private Stream GetFileStream()
{
    return File.OpenRead(@"{PATH}");
}

Im vorherigen Beispiel entspricht der {PATH}-Platzhalter dem Pfad zu der Datei. Das Präfix @ gibt an, dass die Zeichenfolge ein ausführliches Zeichenfolgenliteral ist, das die Verwendung von umgekehrten Schrägstrichen (\) in einem Windows-Betriebssystempfad und eingebettete doppelte Anführungszeichen ("") für ein einzelnes Anführungszeichen im Pfad ermöglicht. Vermeiden Sie alternativ das Zeichenfolgenliteral (@) und verwenden Sie einen der folgenden Ansätze:

  • Verwenden Sie mit Escapezeichen versehene umgekehrte Schrägstriche (\\) und Anführungszeichen (\").
  • Verwenden Sie Schrägstriche (/) im Pfad, die in ASP.NET Core-Apps plattformübergreifend unterstützt werden, und mit Escapezeichen versehene Anführungszeichen (\").

Herunterladen über eine URL

Dieser Abschnitt gilt für relativ große Dateien (in der Regel 250 MB oder größer).

Das Beispiel in diesem Abschnitt verwendet eine Downloaddatei namens quote.txt, die in einem Ordner mit dem Namen files im Webstammverzeichnis der App (Ordner wwwroot) platziert wird. Die Verwendung des Ordners files dient nur zur Demonstrationszwecken. Sie können herunterladbare Dateien in einem von Ihnen bevorzugten beliebigen Ordnerlayout im Webstammverzeichnis (Ordner wwwroot) organisieren und die Dateien u. a. direkt über den Ordner wwwroot bereitstellen.

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/)

Die folgende JS-Funktion triggerFileDownload:

  • Erstellen von HTMLAnchorElement (<a>-Element)
  • Erstellen des Dateinamens (fileName) und der URL (url) für den Download
  • Auslösen des Downloads durch Auslösen eines click-Ereignisses für das Ankerelement
  • Entfernen des Ankerelements
<script>
  window.triggerFileDownload = (fileName, url) => {
    const anchorElement = document.createElement('a');
    anchorElement.href = url;
    anchorElement.download = fileName ?? '';
    anchorElement.click();
    anchorElement.remove();
  }
</script>

Hinweis

Allgemeine Anleitungen zum JSSpeicherort und zu unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET CoreBlazor-Apps in ASP.NET Core.

Die folgende Beispielkomponente lädt die Datei aus demselben Ursprung herunter, den auch die App verwendet. Wenn der Dateidownload aus einem anderen Ursprung versucht wird, konfigurieren Sie CORS. Weitere Informationen finden Sie im Abschnitt Cross-Origin Resource Sharing (CORS).

Ändern Sie den Port im folgenden Beispiel so, dass er mit dem localhost-Entwicklungsport Ihrer Umgebung übereinstimmt.

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

Ressourcenfreigabe zwischen verschiedenen Ursprüngen (CORS)

Ohne weitere Schritte zum Aktivieren von CORS (Cross-Origin Resource Sharing) für Dateien, die nicht denselben Ursprung wie die App haben, bestehen heruntergeladene Dateien die vom Browser durchgeführten CORS-Überprüfungen nicht.

Weitere Informationen zu CORS mit ASP.NET Core-Apps und anderen Microsoft-Produkten und -Diensten, die Dateien zum Download hosten, finden Sie in den folgenden Ressourcen:

Zusätzliche Ressourcen