Downloads de arquivo Blazor do ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Este artigo explica como baixar arquivos em aplicativos Blazor.

Downloads de arquivos

Os arquivos podem ser baixados dos próprios ativos estáticos do aplicativo ou de qualquer outro local:

  • Os aplicativos ASP.NET Core usam o Middleware de Arquivo Estático para fornecer arquivos a clientes de aplicativos do lado do servidor.
  • As diretrizes neste artigo também se aplicam a outros tipos de servidores de arquivos que não usam o .NET, como CDNs (Redes de Distribuição de Conteúdo).

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

Ao baixar arquivos de uma origem diferente do aplicativo, as considerações sobre o CORS (Compartilhamento de Recursos entre Origens) se aplicam. Para obter mais informações, confira a seção CORS (compartilhamento de recursos entre origens).

Considerações de segurança

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

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

  • Baixe arquivos de uma área de download de arquivo dedicado no servidor, preferencialmente de uma unidade que não seja do sistema. O uso de um local dedicado facilita a imposição de restrições de segurança a arquivos baixáveis. Desabilite 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 arquivos de usuários ou outras fontes não confiáveis e disponibilize os arquivos para download imediato sem executar verificações de segurança nos arquivos. Saiba mais em 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ário bruto no cliente com interoperabilidade do JavaScript (JS).

Aviso

A abordagem nesta seção lê o conteúdo do arquivo em um JS ArrayBuffer. Essa abordagem carrega todo o arquivo na memória do cliente, o que pode prejudicar o desempenho. Para baixar arquivos relativamente grandes (>= 250 MB), recomendamos seguir as diretrizes na seção Baixar de uma URL.

A seguinte função de downloadFileFromStreamJS:

  • Lê o fluxo fornecido em um ArrayBuffer.
  • Cria um Blob para encapsular o ArrayBuffer.
  • Cria uma URL de objeto para servir como o endereço de download do arquivo.
  • Cria um HTMLAnchorElement (elemento <a>).
  • Atribui o nome do arquivo (fileName) e a URL (url) para o download.
  • Dispara o download disparando um click evento no elemento de âncora.
  • Remove o elemento de âncora.
  • Revoga a URL do objeto (url) chamando URL.revokeObjectURL. Essa é 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 diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos ASP.NET Core Blazor.

O seguinte componente :

  • Usa a interoperabilidade de streaming de bytes nativa para garantir a transferência eficiente do arquivo para o cliente.
  • Tem um método chamado GetFileStream para recuperar um Stream para o arquivo baixado para clientes. Abordagens alternativas incluem recuperar um arquivo do armazenamento ou gerar um arquivo dinamicamente no código C#. Para essa 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 um MemoryStream para servir como o arquivo binário gerado dinamicamente do exemplo.
  • O método DownloadFileFromStream:
    • Recupera o Stream de GetFileStream.
    • Especifica um nome de arquivo quando o arquivo é salvo no computador 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 função downloadFileFromStreamJS 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

<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()
{
    return 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 (\) em um caminho do sistema operacional Windows e aspas duplas inseridas ("") para aspas simples no caminho. Como alternativa, evite o literal da cadeia de caracteres (@) e use uma das seguintes abordagens:

  • Use barras invertidas de escape (\\) e aspas (\").
  • Use barras duplas (/) no caminho, que são compatíveis entre plataformas em aplicativos ASP.NET Core e aspas de escape (\").

Baixar de uma URL

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

O exemplo nesta seção usa um arquivo de download chamado quote.txt, que é colocado em uma pasta chamada files na raiz da Web do aplicativo (pasta wwwroot). O uso da pasta files é apenas para fins de demonstração. Você pode organizar arquivos baixáveis em qualquer layout de pasta dentro da raiz da Web (pasta wwwroot) que preferir, incluindo o fornecimento dos arquivos diretamente da pasta wwwroot.

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

A seguinte função de triggerFileDownloadJS:

  • Cria um HTMLAnchorElement (elemento <a>).
  • Atribui o nome do arquivo (fileName) e a URL (url) para o download.
  • Dispara o download disparando um click evento no elemento de âncora.
  • Remove o elemento de â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 diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos 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 (compartilhamento de recursos entre origens). Para obter mais informações, confira a seção CORS (compartilhamento de recursos entre origens).

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

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

Compartilhamento de Recursos entre Origens (CORS)

Sem executar outras etapas para habilitar o CORS (compartilhamento de recursos entre origens) para arquivos que não têm a mesma origem que o aplicativo, o download de arquivos não passará por verificações de 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