Blazor -  Upload de arquivos


Hoje veremos como fazer upload de arquivos em um projeto Blazor Server usando o componente BlazorInputFile.

O recurso para fazer o upload de arquivos é muito usado em aplicações web e para aplicações Blazor já temos alguns componentes prontos que realizam esta tarefa.

Hoje vamos usar o componente BlazoInputFile e mostrar como fazer o upload de arquivos.

Esta API ainda esta em preview e por isso embora ela esteja funcional você deve verificar por atualizações sempre que for incluir um componente externo ao seu projeto. A API usa streams nos quais somente métodos assíncronos podem ser usados.

Para usar o componente para fazer o Upload de um arquivo basta declarar no componente Razor :

 
<InputFile OnChange="HandleFileSelected" />

 

Para dar suporte ao upload de mais de um arquivo basta usar o atributo multiple :

 
<InputFile multiple OnChange="HandleFileSelected" />

 

Aqui no evento OnChange o método HandleFileSelected deve fazer o tratamento do arquivo selecionado :

@code {
    void HandleFileSelected(IFileListEntry[] files)
    {
        //código para tratar o arquivo
    }
}

Recursos usados:

Criando o projeto Blazor Server no VS Community 2019

bra o VS 2019 Community (versão mínima 16.4) e selecione a opção Create a New Project;

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

Informe o nome do projeto :  Blazor_Upload1, a localização e clique em Create;

A seguir teremos uma janela com duas opções :

  1. Blazor Server App
  2. Blazor WebAssembly App

Selecione a primeira opção - Blazor Server App. Não vamos usar autenticação e vamos habilitar o https.

Clique no botão Create para criar o projeto.

Antes de prosseguir vamos limpar o projeto excluindo os arquivos abaixo e suas referências:

Vamos também ajustar o arquivo NavMenu.razor deixando apenas a opção Home no menu removendo restante do código.

Agora com o projeto criado e os ajustes feitos vamos incluir a referência ao pacote nuget que vamos usar.

Configurando o projeto : Referenciando o pacote BlazorInputFile

Clique no menu Tools > NuGet Package Manager > Manage NuGet Package For Solution.

E na guia Browse procure pelo pacote BlazorInputFile e instale o pacote no seu projeto:

NotaVocê também pode instalar usando a janela do Gerenciador de pacotes com o comando:
Install-Package BlazorInputFile -Version 0.1.0-preview-00002

Abra o arquivo _Imports.razor  do projeto e inclua as referência aos namespaces:

@using Blazor_Upload1.Services - usado para referenciar os serviços;
@using Microsoft.Extensions.Hosting -
usado para referenciar os métodos de extensão do ambiente;
@using Microsoft.AspNetCore.Hosting -
usado para referenciar IwebHostEnvironment;
@using System.Threading.Tasks -
usado para referenciar os recursos de await e async;
@using BlazorInputFile -
Usado para referenciar o componente;
@using System.IO -
usado para referenciar os métodos da classe IO no tratamento de arquivos;

Essas referências serão usadas em nosso projeto pelos componentes que iremos criar.

@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.JSInterop
@using Blazor_Upload1
@using Blazor_Upload1.Shared
@using Blazor_Upload1.Services
@using Microsoft.Extensions.Hosting
@using Microsoft.AspNetCore.Hosting
@using System.Threading.Tasks
@using BlazorInputFile
@using System.IO

Agora abra o arquivo _Host.cshtml da pasta Pages inclua uma referência ao arquivo Javascript:

 <script src="_content/BlazorInputFile/inputfile.js"></script>

@page "/"
@namespace Blazor_Upload1.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Blazor_Upload1</title>
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
</head>
<body>
    <app>
        <component type="typeof(App)" render-mode="ServerPrerendered" />
    </app>
    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.server.js"></script>
    <script src="_content/BlazorInputFile/inputfile.js"></script>
</body>
</html>

Criando um serviço para fazer o Upload de arquivos

Vamos criar uma pasta chamada Services no projeto e nesta pasta criar a Interface IFileUpload e sua implementação via classe concreta FileUpload.

Crie o arquivo IFileUpload.cs e inclua o código abaixo:

using BlazorInputFile;
using System.Threading.Tasks;
namespace Blazor_Upload1.Services
{
    public interface IFileUpload
    {
        Task UploadAsync(IFileListEntry arquivo);
    }
}

Definimos a assinatura do método UploadAsync que recebe um parâmtero do tipo IFileListEntry.

Crie o arquivo FileUpload.cs e inclua o código que define a implementação da interface:

using BlazorInputFile;
using Microsoft.AspNetCore.Hosting;
using System.IO;
using System.Threading.Tasks;
namespace Blazor_Upload1.Services
{
    public class FileUpload : IFileUpload
    {
        private readonly IWebHostEnvironment _environment;
        public FileUpload(IWebHostEnvironment env)
        {
            _environment = env;
        }

        public async Task UploadAsync(IFileListEntry arquivoEntrada)
        {
           try
           {
                var path = Path.Combine(_environment.ContentRootPath, "Uploads",
                arquivoEntrada.Name);

                var ms = new MemoryStream();
                await arquivoEntrada.Data.CopyToAsync(ms);

                using (FileStream arquivo = new FileStream(path, FileMode.Create,
                        FileAccess.Write))
                {
                      ms.WriteTo(arquivo);
                }
          }
          catch(Exception)
          {
            throw;
          }
        }
    }
}

Neste código injetamos uma instância de IWebHostEnvironment para poder obter o caminho raiz do projeto (wwwroot) que estamos usando para compor o nome do caminho do arquivo que será enviado.

Precisamos então criar uma pasta chamada Uploads na raiz do projeto.

Agora temos que registrar o serviço criado no método ConfigureServices da classe Startup:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddScoped<IFileUpload, FileUpload>();
        }

No código estamos usando o tempo de vida AddScoped que cria os serviços por escopo. Assim, em um aplicativo Web, toda requisição web cria um novo escopo de serviço separado. Isso significa que os serviços com escopo são geralmente criados por requisição web.

NotaSugiro que você leia este artigo: Hutch Codes — Dependency Injection Lifetimes in Blazor

Criando o componente Aviso para exibir alertas

Vamos criar o componente Aviso.razor na pasta Shared do projeto para poder exibir avisos ao usuário em nosso projeto.

Crie o arquivo Aviso.razor na pasta Shared com o código abaixo:

@if (Exibir)
{
    <div class="alert alert-secondary mt-4" role="alertdialog">
        @ChildContent
    </div>
}
@code {
    [Parameter]
    public bool Exibir { get; set; }
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

Assim para exibir um aviso basta definir a variável booleana Exibir como true e definir o parâmetro Mensagem.

Criando o componente para Upload de um arquivo

Para realizar o Upload de um arquivo vamos usar o componente Index.razor criado no projeto.

Abra o arquivo Index.razor na pasta Pages e inclua o código abaixo:

@page "/"
@inject IFileUpload  fileUpload
@inject IWebHostEnvironment Env
<h3>Upload de Arquivo : Selecione o arquivo</h3>
<hr />
<InputFile OnChange="HandleFileSelected" />
<hr />
@if (arquivo != null)
{
    <p>Tamanho em bytes: @arquivo.Size</p>
    <p>Data última modificação: @arquivo.LastModified.ToShortDateString()</p>
    <p>Conteúdo : @arquivo.Type</p>
}
<Aviso Exibir="Exibir">
    <h4>@Mensagem</h4>
</Aviso>
@code {
    IFileListEntry arquivo;
    bool Exibir = false;
    [Parameter]
    public string Mensagem { get; set; }
    async Task HandleFileSelected(IFileListEntry[] files)
    {
        Exibir = true;
        try
        {
            arquivo = files.FirstOrDefault();
            if (arquivo != null)
            {
                await fileUpload.UploadAsync(arquivo);
            }
            Mensagem = "Arquivo(s) enviado(s) com Sucesso...";
        }
        catch (Exception ex)
        {
            if (Env.IsDevelopment())
            {
                Mensagem = "Erro ao enviar arquivo(s) ...\n" + ex.StackTrace.ToString();
            }
            else
            {
                Mensagem = "Não foi possível enviar o(s) arquivo(s) ..." + ex.InnerException.ToString();
            }
        }
    }
}

Neste exemplo estamos permitindo apenas a seleção de um único arquivo, assim a matriz de arquivos(files) vai ter zero ou apenas uma entrada. Para dar suporte a múltiplos arquivos basta incluir o atributo multiple.

Agora é só alegria...

Executando o projeto teremos o resultado abaixo:

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

"O temor do Senhor é fonte de vida, para desviar dos laços da morte."
Provérbios 14:26,27

Referências:


José Carlos Macoratti