Udostępnij przez


pobieranie plików w ASP.NET Core Blazor

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z aktualną wersją, zobacz artykuł w wersji .NET 10.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z aktualną wersją, zobacz artykuł w wersji .NET 10.

W tym artykule wyjaśniono, jak pobierać pliki w Blazor aplikacjach.

Operacje pobierania plików

W tym artykule opisano podejścia do następujących scenariuszy, w których plik nie powinien być otwierany przez przeglądarkę, ale pobierany i zapisywany na kliencie:

Podczas pobierania plików z innego źródła niż aplikacja mają zastosowanie zagadnienia dotyczące współużytkowania zasobów między źródłami (CORS). Aby uzyskać więcej informacji, zobacz sekcję Współużytkowanie zasobów między źródłami (CORS).

Zagadnienia dotyczące zabezpieczeń

Należy zachować ostrożność podczas zapewniania użytkownikom możliwości pobierania plików z serwera. Cyberataki mogą wykonywać ataki typu "odmowa usługi" , ataki wykorzystujące interfejs API lub próbować naruszyć bezpieczeństwo sieci i serwerów w inny sposób.

Kroki zabezpieczeń, które zmniejszają prawdopodobieństwo pomyślnego ataku, to:

  • Pobierz pliki z dedykowanego obszaru pobierania plików na serwerze, najlepiej z dysku innego niż system. Użycie dedykowanej lokalizacji ułatwia nakładanie ograniczeń zabezpieczeń na pliki do pobrania. Wyłącz uprawnienia wykonywania w obszarze pobierania pliku.
  • Kontrole zabezpieczeń po stronie klienta są łatwe do obejścia przez złośliwych użytkowników. Zawsze przeprowadzaj kontrole zabezpieczeń po stronie klienta na serwerze.
  • Nie odbieraj plików od użytkowników ani innych niezaufanych źródeł, a następnie udostępniaj pliki do natychmiastowego pobierania bez przeprowadzania kontroli zabezpieczeń plików. Aby uzyskać więcej informacji, zobacz Przekazywanie plików w programie ASP.NET Core.

Pobieranie ze strumienia

Ta sekcja dotyczy plików, które zazwyczaj mają rozmiar do 250 MB.

Zalecanym podejściem do pobierania stosunkowo małych plików (<250 MB) jest przesyłanie strumieniowe zawartości plików do nieprzetworzonego buforu danych binarnych na kliencie za pomocą JavaScript (JS) interop. pl-PL: Takie podejście jest skuteczne w przypadku składników, które przyjmują interaktywny tryb renderowania, ale nie w przypadku składników, które przyjmują statyczne renderowanie po stronie serwera (statyczne SSR).

Zalecanym podejściem do pobierania stosunkowo małych plików (<250 MB) jest przesyłanie strumieniowe zawartości plików do nieprzetworzonego buforu danych binarnych na kliencie za pomocą JavaScript (JS) interop.

Ostrzeżenie

Podejście w tej sekcji odczytuje zawartość pliku do elementu JS ArrayBuffer. Takie podejście powoduje załadowanie całego pliku do pamięci klienta, co może obniżyć wydajność. Aby pobrać stosunkowo duże pliki (>= 250 MB), zalecamy wykonanie wskazówek w sekcji Pobieranie z adresu URL .

Następująca downloadFileFromStreamJS funkcja:

  • Odczytuje podany strumień do elementu ArrayBuffer.
  • Tworzy Blob w celu opakowania ArrayBuffer.
  • Tworzy adres URL obiektu, który będzie służył jako adres pobierania pliku.
  • Tworzy element HTMLAnchorElement (<a> ).
  • Przypisuje nazwę pliku (fileName) i adres URL (url) do pobrania.
  • Wyzwala pobieranie, uruchamiając click zdarzenie na elemencie kotwicy.
  • Usuwa element kotwicy.
  • Odwołuje adres URL obiektu (url) przez wywołanie metody URL.revokeObjectURL. Jest to ważny krok, aby upewnić się, że pamięć nie wycieka po stronie klienta.
<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>

Uwaga

Aby uzyskać ogólne wskazówki dotyczące lokalizacji JS oraz nasze zalecenia dotyczące aplikacji produkcyjnych, zobacz lokalizację JavaScript w aplikacjach ASP.NET CoreBlazor.

Następujący składnik:

  • Używa natywnej interoperacyjności strumieniowego przesyłania bajtów, aby zapewnić wydajny transfer pliku do klienta.
  • Ma metodę o nazwie GetFileStream, aby pobrać Stream plik, który został pobrany przez klientów. Alternatywne podejścia obejmują pobieranie pliku z magazynu lub dynamiczne generowanie pliku w kodzie języka C#. Na potrzeby tego pokazu aplikacja tworzy plik 50 KB danych losowych z nowej tablicy bajtów (new byte[]). Bajty są opakowane w MemoryStream, aby służyć jako dynamicznie wygenerowany plik binarny dla przykładu.
  • Metoda DownloadFileFromStream :
    • Pobiera element Stream z GetFileStream.
    • Określa nazwę pliku, gdy plik jest zapisywany na komputerze użytkownika. Poniższy przykład nazywa plik quote.txt.
    • Zawija znacznik Stream w DotNetStreamReference, co umożliwia strumieniowe przesyłanie danych pliku do klienta.
    • Wywołuje funkcję downloadFileFromStreamJS w celu zaakceptowania danych na kliencie.

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

W przypadku składnika w aplikacji po stronie serwera, który musi zwrócić Stream wartość dla pliku fizycznego, składnik może wywołać File.OpenRead, jak pokazano w poniższym przykładzie.

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

W poprzednim przykładzie symbol zastępczy {PATH} oznacza ścieżkę do pliku. Prefiks @ wskazuje, że ciąg jest dosłownym ciągiem znaków, co pozwala na użycie backslashy (\) w ścieżce w systemie operacyjnym Windows oraz osadzonych podwójnych cudzysłowów ("") w przypadku cytowania ścieżki. Alternatywnie należy unikać literału ciągu (@) i użyć jednej z następujących metod:

  • Użyj znaków escape'owych (\\) i cudzysłowów (\").
  • Użyj ukośników (/) w ścieżce, które są obsługiwane na różnych platformach w aplikacjach ASP.NET Core, oraz uciekanych cudzysłowów (\").

Pobieranie z adresu URL

Ta sekcja dotyczy plików, które są stosunkowo duże, zazwyczaj 250 MB lub większe.

Zalecaną metodą pobierania relatywnie dużych plików (>= 250 MB) z interaktywnie renderowanych składników lub plików o dowolnym rozmiarze dla statycznie renderowanych składników jest użycie JS, aby wyzwolić element kotwicy z nazwą i adresem URL pliku.

Zalecane podejście do pobierania stosunkowo dużych plików (>= 250 MB) polega na użyciu JS do uruchomienia elementu kotwicy z nazwą i adresem URL pliku.

W przykładzie w tej sekcji jest używany plik pobierania o nazwie quote.txt, który znajduje się w folderze o nazwie files w katalogu głównym aplikacji (wwwroot folder). Korzystanie z files folderu jest przeznaczone tylko do celów demonstracyjnych. Pliki do pobrania można organizować w dowolnym układzie folderów w obrębie katalogu głównego serwisu (wwwroot katalogu), w tym obsługując pliki bezpośrednio z wwwroot folderu.

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

Następująca triggerFileDownloadJS funkcja:

  • Tworzy element HTMLAnchorElement (<a> ).
  • Przypisuje nazwę pliku (fileName) i adres URL (url) do pobrania.
  • Wyzwala pobieranie, uruchamiając click zdarzenie na elemencie kotwicy.
  • Usuwa element kotwicy.
<script>
  window.triggerFileDownload = (fileName, url) => {
    const anchorElement = document.createElement('a');
    anchorElement.href = url;
    anchorElement.download = fileName ?? '';
    anchorElement.click();
    anchorElement.remove();
  }
</script>

Uwaga

Aby uzyskać ogólne wskazówki dotyczące lokalizacji JS oraz nasze zalecenia dotyczące aplikacji produkcyjnych, zobacz lokalizację JavaScript w aplikacjach ASP.NET CoreBlazor.

Poniższy przykładowy składnik pobiera plik z tego samego źródła, którego używa aplikacja. Jeśli próba pobrania pliku zostanie podjęta z innego źródła, skonfiguruj współużytkowanie zasobów między źródłami (CORS). Aby uzyskać więcej informacji, zobacz sekcję Współużytkowanie zasobów między źródłami (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);
    }
}

W przypadku składników interaktywnych przycisk w poprzednim przykładzie wywołuje DownloadFileFromURL procedurę obsługi w celu wywołania funkcji JSJavaScript (triggerFileDownload).

Jeśli składnik przyjmuje statyczne renderowanie po stronie serwera (statyczne SSR), dodaj procedurę obsługi zdarzeń dla przycisku (addEventListener), aby wywołać triggerFileDownload zgodnie z wytycznymi zawartymi w ASP.NET Core Blazor JavaScript ze statycznym renderowaniem po stronie serwera.

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

W przypadku składników interaktywnych przycisk w poprzednim przykładzie wywołuje DownloadFileFromURL procedurę obsługi w celu wywołania funkcji JSJavaScript (triggerFileDownload).

Jeśli składnik przyjmuje statyczne renderowanie po stronie serwera (statyczne SSR), dodaj procedurę obsługi zdarzeń dla przycisku (addEventListener), aby wywołać triggerFileDownload zgodnie z wytycznymi zawartymi w ASP.NET Core Blazor JavaScript ze statycznym renderowaniem po stronie serwera.

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

Zmień port w poprzednim przykładzie, aby był zgodny z portem rozwojowym w środowisku 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);
    }
}

Zmień port w poprzednim przykładzie, aby był zgodny z portem rozwojowym w środowisku localhost.

Udostępnianie zasobów z różnych źródeł (CORS)

Bez wykonywania dalszych kroków w celu włączenia współużytkowania zasobów między źródłami (CORS) dla plików, które nie mają tego samego źródła co aplikacja, pobieranie plików nie będzie przechodzić testów CORS wykonanych przez przeglądarkę.

Aby uzyskać więcej informacji na temat mechanizmu CORS z aplikacjami ASP.NET Core i innymi produktami i usługami firmy Microsoft hostujących pliki do pobrania, zobacz następujące zasoby:

Dodatkowe zasoby