Blazor Server - Upload de arquivos com InputFile

Hoje veremos como enviar arquivos para o servidor em uma aplicação Blazor Server usando o componente InputFile.

Se você esta chegando agora e não sabe o que é o Blazor leia o artigo ASP .NET Core - Iniciando com o Blazor - Macoratti; se você já conhece e quer saber mais pode fazer o curso de Blazor Essencial.  

Eu já mostrei como enviar arquivos no Blazor usando o componente BlazorInputFile que não era um componente nativo do Blazor. A partir do  .NET 5, o Blazor apresenta o componente nativo InputFile que pode ser usado para ler dados de arquivos via navegador tanto no Blazor Server como no Blazor WebAssembly.

O  componente InputFile renderiza um elemento HTML <input> do tipo file. Por padrão, o usuário seleciona arquivos individuais,e , para permitir que  o usuário carregue vários arquivos ao mesmo tempo basta adicionar o atributo multiple.

Exemplo:

<InputFile OnChange="@CarregaArquivos" multiple />

@code {
    private void CarregaArquivos(InputFileChangeEventArgs e)
    {
        ...
    }
}

No código acima o componente executa o método CarregaArquivos  quando o evento OnChange ocorrer.

Vejamos a seguir como fazer isso na prática.

Recursos usados:

Blazor Server - Usando InputFile

Abra o VS 2022 Community e selecione a opção Create a New Project;

A seguir selecione a opção Blazor Server App e clique em next;

Informe o nome do projeto :  BlazorServerUpload;

A seguir define as configurações conforme a figura abaixo e clique em Create;

Com o projeto criado vamos fazer as seguintes alterações, ajustes e configurações :

Exclua os arquivos abaixo e suas referências:

No arquivo _Imports.razor vamos incluir uma referência a : 1-Microsoft.AspNetCore.Components.Forms e 2-System.IO

@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorServerUpload
@using BlazorServerUpload.Shared
@using System.IO

O primeiro permite acesso ao componente InptuFile e o seguinte permite criar e escrever em arquivos no servidor.

No arquivo NavMenu.razor da pasta Shared inclua mais um item de menu para exibir a opção Upload:

...
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
       <li class="nav-item px-3">
             <NavLink class="nav-link" href="/fileupload">
                <span class="oi oi-cloud-upload" aria-hidden="true"></span> Upload
             </NavLink>
   
   </li>
    </ul>
</div>
...

Crie uma pasta images dentro de wwwroot e inclua nesta pasta a imagem upload1.jpg que iremos usar no projeto.

A seguir altere o código do arquivo Index.razor para:

@page "/"

<PageTitle>Index</PageTitle>

<img src="/images/upload1.jpg" />

<h3>Blazor Server - InputFile</h3>

A seguir vamos criar um novo componente chamado FileUpload na pasta Pages do projeto e incluir o código abaixo neste arquivo:

@page "/fileupload"
@inject IWebHostEnvironment environment
@inject ILogger<FileUpload> Logger

<h1>Blazor Server File Upload</h1>

<h5>@Message</h5>

<form @onsubmit="OnSubmit">
    <InputFile OnChange="OnInputFileChange" multiple />
    <br /><br />
    <button type="submit">Enviar Arquivo(s) selecionado(s) para Upload</button>
</form>

@code {
    string Message = "Nenhum arquivo selecionado";
    IReadOnlyList<IBrowserFile>? arquivoSelecionados = null;

    private void OnInputFileChange(InputFileChangeEventArgs e)
    {
        arquivoSelecionados = e.GetMultipleFiles();
        Message = $"{arquivoSelecionados.Count} arquivo(s) selecionado(s)";
    }

    private async void OnSubmit()
    {
        if (arquivoSelecionados is not null)
        {
            foreach (var file in arquivoSelecionados)
            {
                try
                {
                    Stream stream = file.OpenReadStream();
                    var path = $"{environment.WebRootPath}\\Uploads\\{file.Name}";
                    FileStream fs = File.Create(path);
                    await stream.CopyToAsync(fs);
                    stream.Close();
                    fs.Close();
                    Message = $" Enviar {arquivoSelecionados.Count} arquivo(s) enviado(s) para o servidor"
;
                }
                catch (Exception ex)
                {
                    Message = $"Erro ao enviar arquivo : {file.Name} : {ex.Message}";
                    Logger.LogError("File: {Filename} Error: {Error}", file.Name, ex.Message);
                }
            }
        }
        else
        {
            Message = $"Selecione um arquivo ...";
        }
    }
}

Vamos entender o código :

1- Injetamos uma instância de IWebHostEnvironment para descobrir o caminho de wwwroot no servidor para salvar os arquivos carregados; O ILogger é usado para logar os erros ocorridos;

@inject IWebHostEnvironment environment
@inject ILogger<FileUpload> Logger

2- A propriedade Message apenas exibe mensagens:

<h5>@Message</h5>

3- Criamos um formulário que será submetido no evento OnSubmit onde estamos usando o componente InputFile usando o atributo multiple que permite selecionar mais de um arquivo, e que define o método OnInputFileChange onde vamos tratar a exibição de quantos arquivos foram selecionados.

<form @onsubmit="OnSubmit">
    <InputFile OnChange="OnInputFileChange" multiple />
    <br /><br />
    <button type="submit">Enviar Arquivo(s) selecionado(s) para Upload</button>
</form>

O botão Enviar apenas submete o formulário.

No bloco de código do arquivo temos:

4- A definição do texto da mensagem inicial e a definição da variável arquivoSelecionados como sendo uma lista somente leitura do tipo IBrowserFile que representa os dados de um arquivo selecionado pelo InputFile :

string Message = "Nenhum arquivo selecionado";
IReadOnlyList<IBrowserFile>? arquivoSelecionados = null;

5- O método OnInputFileChange trata o evento OnChange do componente InputFile e obtém a lista de arquivos selecionados alterando o texto da mensagem:

 private void OnInputFileChange(InputFileChangeEventArgs e)
    {
        arquivoSelecionados = e.GetMultipleFiles();
        Message = $"{arquivoSelecionados.Count} arquivo(s) selecionado(s)";
    }

6- No método assíncrono OnSubmit estamos tratando o envio do formulário.

Primeiro verificamos se existem arquivos selecionados para a seguir percorrer a lista de arquivos usando um bloco foreach e ler cada arquivo e a seguir usamos o método OpenReadStream() para abrir um fluxo através do qual o conteúdo do arquivo carregado pode ser lido. Lembre-se de que este é um aplicativo Blazor Server e seu código está sendo executado no lado do servidor. Portanto, combinamos as propriedades WebRootPath e Name do objeto IBrowserFile para chegar ao caminho final do lado do servidor para o arquivo.

Como queremos salvar o arquivo carregado no servidor, criamos um novo FileStream para o arquivo. Em seguida, copiamos o conteúdo do arquivo para o FileStream usando o método CopyToAsync(). Por fim, fechamos os dois fluxos. Aqui, estamos fazendo tratamento de erros básico mas não estamos verificando o tamanho do arquivo, o que deve ser considerado em uma aplicação de produção:

 private async void OnSubmit()
    {
        if (arquivoSelecionados is not null)
        {
            foreach (var file in arquivoSelecionados)
            {
                try
                {
                    Stream stream = file.OpenReadStream();
                    var path = $"{environment.WebRootPath}\\Uploads\\{file.Name}";
                    FileStream fs = File.Create(path);
                    await stream.CopyToAsync(fs);
                    stream.Close();

                    fs.Close();
                    Message = $" Enviar {arquivoSelecionados.Count} arquivo(s) enviado(s) para o servidor";
                }
                catch (Exception ex)
                {
                    Message = $"Erro ao enviar arquivo : {file.Name} : {ex.Message}";
                    Logger.LogError("File: {Filename} Error: {Error}", file.Name, ex.Message);
                }
            }
        }
        else
        {
            Message = $"Selecione um arquivo ...";
        }
    }

Atenção !!! Os arquivos enviados estão sendo salvos na pasta Uploads que deverá ser criada dentro da pasta wwwroot.

Agora é só alegria...

Executando o projeto teremos o resultado abaixo:

Em outro artigo veremos como fazer a mesma coisa em um projeto Blazor WebAssembly.

Pegue o projeto aqui:   BlazorServerUpload.zip (sem as referências)

"Bom é ter esperança, e aguardar em silêncio a salvação do Senhor."
Lamentações 3:26


Referências:


José Carlos Macoratti