Blazor - Upload com Drag and Drop (.NET 5)

Hoje vamos usar o componente InputFile para enviar arquivos e permitir o recurso drag and drop para selecionar os arquivos a enviar.

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.  

No artigo Blazor - Upload de arquivos , eu mostrei como enviar arquivos usando o componente InputFile.

Hoje vamos retomar o uso do componente para enviar arquivos mas vamos incluir a funcionalidade de poder arrastar e soltar arquivos para fazer o Upload.

Vamos criar o projeto no ambiente  .NET 5.0 RC1 e usar o Visual Studio 2019 Community Preview na versão 16.8.0 Preview 6.

Recursos usados:

Criando o projeto no VS Community 2019

Abra o VS 2019 Community (versão mínima 16.6) 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_DragDropUpload a localização e clique em Create;

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

Clique no botão Create para criar o projeto.

Com o projeto criado vamos limpar o projeto excluindo os arquivos abaixo e suas referências:

Também remova do arquivo NavMenu.razor as opções de Menu para Counter e FetchData deixando apenas o link do menu para Home.

Alterando o código do arquivo Index.razor

Vamos definir o código para realizar o Upload e permitir o drag and drop no arquivo Index.razor da pasta Pages.

No arquivo _Imports.razor vamos incluir 2 namespaces: System.IO e Microsoft.AspNetCore.Hosting:

@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 Blazor_DragDropUpload
@using Blazor_DragDropUpload.Shared
@using System.IO;
@using Microsoft.AspNetCore.Hosting;

A seguir abra o arquivo Index.razor e inclua o código abaixo:

@page "/"
@inject IWebHostEnvironment _env;
<h3>.NET 5.0 Blazor InputFile</h3>
<p>Usando o componente : <b>InputFile</b> </p>
<div>
    <div class="inputArea">
        <InputFile id="inputDefault"
                   OnChange="OnInputFileChange"
                   accept="image/png,image/gif,image/jpeg" />
    </div>
    <div class="dropArea @dropClass">
        Arraste e solte seus arquivos aqui ou clique em Abrir Arquivos para abrir o diálogo...
        <InputFile id="inputDrop"
                   OnChange="OnInputFileChange"
                   @ondragenter="HandleDragEnter"
                   @ondragleave="HandleDragLeave"
                   multiple />
    </div>
    @if (arquivos != null && arquivos.Count > 1)
    {
        <div>
            <ul>
                @foreach (var arquivo in arquivos)
                {
                    <li>@arquivo.Name</li>
                }
            </ul>
        </div>
    }
    @if (urls.Count > 0)
    {
        foreach (var url in urls)
        {
            <br />
            <a href="@url" download>@url</a>
        }
    }
</div>
@code {
    IReadOnlyList<IBrowserFile> arquivos;
    List<string> urls = new List<string>();
    string dropClass = string.Empty;
    const int maxFileSize = 10485760;
    private void HandleDragEnter()
    {
        dropClass = "dropAreaDrug";
    }
    private void HandleDragLeave()
    {
        dropClass = string.Empty;
    }
    async Task OnInputFileChange(InputFileChangeEventArgs e)
    {
        dropClass = string.Empty;
        try
        {
            if (e.FileCount > 1)
            {
                arquivos = e.GetMultipleFiles();
                urls.Clear();
                urls.AddRange(await SalvarArquivos(arquivos));
            }
            else
            {
                arquivos = null;
                var url = await SalvarArquivo(e.File);
                urls.Clear();
                urls.Add(url);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
            throw;
        }
    }
    private async Task<List<string>> SalvarArquivos(IReadOnlyList<IBrowserFile> arquivos)
    {
        var list = new List<string>();
        var guid = Guid.NewGuid().ToString();
        foreach (var file in arquivos)
        {
            var url = await SalvarArquivo(file, guid);
            list.Add(url);
        }
        return list;
    }
    private async Task<string> SalvarArquivo(IBrowserFile file, string guid = null)
    {
        if (guid == null)
        {
            guid = Guid.NewGuid().ToString();
        }

        var relativePath = Path.Combine("uploads", guid);
        var dirToSave = Path.Combine(_env.WebRootPath, relativePath);
        var di = new DirectoryInfo(dirToSave);

        if (!di.Exists)
        {
            di.Create();
        }

        var filePath = Path.Combine(dirToSave, file.Name);

        using (var stream = file.OpenReadStream(maxFileSize))
        {
            using (var mstream = new MemoryStream())
            {
                await stream.CopyToAsync(mstream);
                await File.WriteAllBytesAsync(filePath, mstream.ToArray());
            }
        }

        var url = Path.Combine(relativePath, file.Name).Replace("\\", "/");
        return url;
    }
}

Vamos entender este código:

1- No código abaixo incluímos dois componentes InputFile. O primeiro atua como um InputFile normal e o seguinte permite realizar o drag and drop:

<div>
    <div class="inputArea">
        <InputFile id="inputDefault"
                   OnChange="OnInputFileChange"
                   accept="image/png,image/gif,image/jpeg" />

    </div>

    <div class="dropArea @dropClass">
        Arraste e solte seus arquivos aqui ou clique em Abrir Arquivos para abrir o diálogo...
        <InputFile id="inputDrop"
                   OnChange="OnInputFileChange"
                   @ondragenter="HandleDragEnter"
                   @ondragleave="HandleDragLeave"
                   multiple />

    </div>

   .......

O primeiro componente InputFile adicionado permite controlar os arquivos selecionados, o método OnInputFileChange () é atribuído ao evento OnChange. Esse método será declarado um pouco mais adiante na seção @code do Blazor. Além disso, este componente aceita apenas um único arquivo de imagem png, gif ou jpeg.

O segundo controle InputFile será usado para aceitar vários arquivos de qualquer tipo. Ele também usa o método OnInputFileChange() para lidar com os arquivos selecionados. Dois manipuladores adicionais de eventos @ondragenter e @ondragleave são usados para alterar o valor da variável dropClass que armazena o nome de uma classe CSS.

A seguir temos o código de marcação que é responsável por exibir os nomes dos arquivos a serem carregados. A lista de arquivos será definida na seção @code posteriormente :

 @if (arquivos != null && arquivos.Count > 1)
    {
        <div>
            <ul>
                @foreach (var arquivo in arquivos)
                {
                    <li>@arquivo.Name</li>
                }
            </ul>
        </div>
    }

E para concluir temos o código que exibe os links dos arquivos enviados. A lista da url será definida na seção @code a seguir :

@if (urls.Count > 0)
    {
        foreach (var url in urls)
        {
            <br />
            <a href="@url" download>@url</a>
        }
    }

O código da seção @Code contém campos e métodos C# :

A lista de arquivos responsável por armazenar os arquivos para upload e a lista de urls responsável por armazenar os links para os arquivos enviados.

A variável dropClass captura o nome da classe CSS que é aplicada quando o usuário arrasta arquivos no segundo InputFile.

E a constante maxFileSize restringe o tamanho do arquivo único.

Os manipuladores HandleDragEnter() e HandleDragLeave() que definem e limpam o valor da variável dropClass.

O trabalho principal é feito dentro do manipulador OnInputFileChange() que é responsável por salvar um ou vários arquivos com o auxílio dos métodos privados SalvarArquivo() e SalvarArquivos().

Os arquivos são armazenados no diretório wwwroot/upload. E em cada upload, uma nova pasta com um nome GUID exclusivo é criada dentro desse diretório.

Para concluir vamos incluir no arquivo CSS site.css dentro da pasta wwwroot/css o código abaixo que aplica um estilo ao controle usado para onde vamos arrastar os arquivos:

Executando o projeto teremos o resultado a seguir:

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

"Porque a lei foi dada por Moisés; a graça e a verdade vieram por Jesus Cristo."
João 1:17

 


Referências:


José Carlos Macoratti