Partilhar via


ASP.NET Core Blazor downloads de arquivos

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.

Advertência

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 10 deste artigo.

Este artigo explica como baixar arquivos em Blazor aplicativos.

Downloads de arquivos

Este artigo aborda abordagens para os seguintes cenários, onde um arquivo não deve ser aberto por um navegador, mas baixado e salvo no cliente:

Ao baixar arquivos de uma origem diferente do aplicativo, aplicam-se as considerações de Compartilhamento de Recursos entre Origens (CORS). Para obter mais informações, consulte a seção Cross-Origin Resource Sharing (CORS).

Considerações de segurança

Tenha cuidado ao fornecer aos usuários a capacidade de baixar arquivos de um servidor. Os ciberatacantes podem executar ataques de negação de serviço (DoS), ataques de exploração de API ou tentar comprometer redes e servidores de outras formas.

As etapas de segurança que reduzem a probabilidade de um ataque bem-sucedido são:

  • Transfira ficheiros a partir de uma área dedicada de transferência de ficheiros no servidor, de preferência a partir de uma unidade que não seja do sistema. A utilização de uma localização dedicada facilita a imposição de restrições de segurança aos ficheiros transferíveis. Desative as permissões de execução na área de download de arquivos.
  • As verificações de segurança do lado do cliente são fáceis de contornar por usuários mal-intencionados. Sempre execute verificações de segurança do lado do cliente no servidor também.
  • Não receba ficheiros de utilizadores ou de outras fontes não fidedignas e, em seguida, disponibilize os ficheiros para transferência imediata sem executar verificações de segurança nos ficheiros. Para obter mais informações, consulte Carregar arquivos no ASP.NET Core.

Download de um fluxo

Esta seção se aplica a arquivos que normalmente têm até 250 MB de tamanho.

A abordagem recomendada para baixar arquivos relativamente pequenos (<250 MB) é transmitir o conteúdo do arquivo para um buffer de dados binários brutos no cliente com interoperabilidade JavaScript (JS). Essa abordagem é eficaz para componentes que adotam um modo de renderização interativo, mas não para componentes que adotam renderização estática do lado do servidor (SSR estático).

A abordagem recomendada para baixar arquivos relativamente pequenos (<250 MB) é transmitir o conteúdo do arquivo para um buffer de dados binários brutos no cliente com interoperabilidade JavaScript (JS).

Advertência

A abordagem nesta seção lê o conteúdo do arquivo para um JS ArrayBuffer. Essa abordagem carrega o arquivo inteiro na memória do cliente, o que pode prejudicar o desempenho. Para transferir ficheiros relativamente grandes (>= 250 MB), recomendamos que siga as orientações na secção Transferir a partir de um URL .

A seguinte função downloadFileFromStreamJS:

  • Lê o fluxo fornecido em um ArrayBuffer.
  • Cria um Blob para envolver o ArrayBuffer.
  • Cria uma URL de objeto para servir como endereço de download do arquivo.
  • Cria um HTMLAnchorElement elemento (<a> ).
  • Atribui o nome (fileName) e o URL (url) do arquivo para download.
  • Aciona o download disparando um click evento no elemento âncora.
  • Remova o elemento âncora.
  • Revoga a URL do objeto (url) chamando URL.revokeObjectURL. Esta é uma etapa importante para garantir que a memória não seja vazada no cliente.
<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>

Observação

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações ASP.NET Core Blazor.

O seguinte componente:

  • Usa interoperabilidade nativa de streaming de bytes para garantir a transferência eficiente do arquivo para o cliente.
  • Tem um método nomeado GetFileStream para recuperar um Stream para o ficheiro que é baixado para os clientes. Abordagens alternativas incluem recuperar um arquivo do armazenamento ou gerar um arquivo dinamicamente em código C#. Para esta demonstração, o aplicativo cria um arquivo de 50 KB de dados aleatórios de uma nova matriz de bytes (new byte[]). Os bytes são encapsulados com a MemoryStream para servir como o arquivo binário gerado dinamicamente do exemplo.
  • O DownloadFileFromStream método:
    • Recupera o Stream de GetFileStream.
    • Especifica um nome de arquivo quando o arquivo é salvo na máquina do usuário. O exemplo a seguir nomeia o arquivo quote.txt.
    • Encapsula o Stream em um DotNetStreamReference, que permite transmitir os dados do arquivo para o cliente.
    • Invoca a downloadFileFromStreamJS função para aceitar os dados no cliente.

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

Para um componente em um aplicativo do lado do servidor que deve retornar um Stream para um arquivo físico, o componente pode chamar File.OpenRead, como demonstra o exemplo a seguir:

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

No exemplo anterior, o espaço reservado {PATH} é o caminho para o arquivo. O prefixo @ indica que a cadeia de caracteres é um literal de cadeia de caracteres verbatim, que permite o uso de barras invertidas (\) num caminho do sistema operativo Windows e aspas duplas incorporadas ("") para uma aspa simples no caminho. Como alternativa, evite a cadeia de caracteres literal (@) e use uma das seguintes abordagens:

  • Use barras invertidas com escape (\\) e aspas (\").
  • Use barras (/) no caminho, que são suportadas entre plataformas em aplicativos ASP.NET Core, e aspas com escape (\").

Transferir a partir de um URL

Esta secção aplica-se a ficheiros relativamente grandes, normalmente com 250 MB ou mais.

A abordagem recomendada para baixar arquivos relativamente grandes (>= 250 MB) com componentes renderizados interativamente ou arquivos de qualquer tamanho para componentes renderizados estaticamente é usar JS para acionar um elemento âncora com o nome e a URL do arquivo.

A abordagem recomendada para baixar arquivos relativamente grandes (>= 250 MB) é usar JS para acionar um elemento âncora com o nome e a URL do arquivo.

O exemplo nesta seção usa um arquivo de download chamado quote.txt, que é colocado em uma pasta nomeada files na raiz da Web do aplicativo (wwwroot pasta). O uso da pasta files é apenas para fins de demonstração. Você pode organizar ficheiros para download em qualquer layout de pasta dentro do diretório raiz da web (wwwroot pasta) que preferir, inclusive servindo os ficheiros diretamente da wwwroot pasta.

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

A seguinte função triggerFileDownloadJS:

  • Cria um HTMLAnchorElement elemento (<a> ).
  • Atribui o nome (fileName) e o URL (url) do arquivo para download.
  • Aciona o download disparando um click evento no elemento âncora.
  • Remova o elemento âncora.
<script>
  window.triggerFileDownload = (fileName, url) => {
    const anchorElement = document.createElement('a');
    anchorElement.href = url;
    anchorElement.download = fileName ?? '';
    anchorElement.click();
    anchorElement.remove();
  }
</script>

Observação

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações ASP.NET Core Blazor.

O componente de exemplo a seguir baixa o arquivo da mesma origem que o aplicativo usa. Se o download do arquivo for tentado de uma origem diferente, configure o CORS (Cross-Origin Resource Sharing). Para obter mais informações, consulte a seção Cross-Origin Resource Sharing (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);
    }
}

Para componentes interativos, o botão no exemplo anterior chama o manipulador DownloadFileFromURL para invocar a função JavaScript JS (triggerFileDownload).

Se o componente adotar renderização estática do lado do servidor (SSR estático), adicione um manipulador de eventos para o botão (addEventListener) a ser chamado triggerFileDownload seguindo as orientações do ASP.NET Core Blazor JavaScript com renderização estática do lado do servidor (SSR estático).

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

Para componentes interativos, o botão no exemplo anterior chama o manipulador DownloadFileFromURL para invocar a função JavaScript JS (triggerFileDownload).

Se o componente adotar renderização estática do lado do servidor (SSR estático), adicione um manipulador de eventos para o botão (addEventListener) a ser chamado triggerFileDownload seguindo as orientações do ASP.NET Core Blazor JavaScript com renderização estática do lado do servidor (SSR estático).

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

Altere a porta no exemplo anterior para corresponder à porta de desenvolvimento localhost do seu ambiente.

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

Altere a porta no exemplo anterior para corresponder à porta de desenvolvimento localhost do seu ambiente.

Compartilhamento de recursos entre origens (CORS)

Sem tomar medidas adicionais para habilitar o CORS (Cross-Origin Resource Sharing) para arquivos que não têm a mesma origem do aplicativo, o download de arquivos não passará pelas verificações CORS feitas pelo navegador.

Para obter mais informações sobre o CORS com aplicativos ASP.NET Core e outros produtos e serviços da Microsoft que hospedam arquivos para download, consulte os seguintes recursos:

Recursos adicionais