Blazor - Upload de arquivos (Static Server) - II


   Neste artigo veremos como realizar o upload de arquivos no Blazor usando o modo de renderização Static Server Rendering.

Continuando o artigo anterior vamos agora criar os componentes Blazor para fazer o upload de arquivos e para visualizar os arquivos enviados armazenados no SQLite.

Definindo o menu

Na pasta Components/Layout vamos ajustar o código do arquivo NavMenu  para exibir as opções do menu e para acessar os componenes que iremos criar conforme abaixo:

...
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
    <nav class="flex-column">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
            </NavLink>
        </div>
       <div class="nav-item px-3">
            <NavLink class="nav-link" href="uploadssr">
                <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Uploads
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="exibirarquivos">
                <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Ver Arquivos
            </NavLink>
        </div>
    </nav>
</div>

A seguir vamos incluir no arquivo _Imports.razor o código abaixo:

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorUploads
@using BlazorUploads.Components

@using Microsoft.AspNetCore.Mvc
@
using System.Net
@
using System.ComponentModel.DataAnnotations
@inject
IWebHostEnvironment env
@inject
IUploadRepository _repository
 

Este código vai permitir compartilhar os recursos usados com todos os componentes do projeto.

Para concluir esta parte, vamos ajustar o código do componente Home.razor conforme o código a seguir:

@page "/"
<PageTitle>Home</PageTitle>
<h1>Upload de arquivos</h1>
<img src="/uploads1.jpg"/>

Criando o componente para enviar arquivos

Vamos criar na pasta Components/Pages o componente UploadSSR.razor  que vai enviar arquivos para o servidor:

@page "/uploadssr"
@using BlazorUploadSSR.Entities
<div>
    <h3>Upload Static Server</h3>
    <p>Blazor Server-side Rendering</p>
</div>
@if (Mensagem.Length > 0)
{
    <p class="alert alert-secondary" role="alert">@Mensagem</p>
}
<EditForm Model="@Arquivo" OnValidSubmit="@EnviarArquivos" FormName="FormUpload" 
    method="post" enctype="multipart/form-data">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <InputText class="form-control mb-4" placeholder="Titulo" @bind-Value="@Arquivo.Titulo" />
    <InputText class="form-control mb-4" placeholder="Descricao" @bind-Value="@Arquivo.Descricao" />
    <InputFile class="form-control mb-4" placeholder="Anexos" name="Arquivo.Anexos" multiple />
    <button class="btn btn-primary" type="submit">Enviar Arquivos</button>
</EditForm>
@code {
    [SupplyParameterFromForm(FormName = "FormUpload")]
    private ArquivoUpload Arquivo { get; set; } = new();
    private string Mensagem { get; set; } = String.Empty;
    private long tamanhoMaximoArquivo = 1 * 1024 * 1024; //1 MB;
    private async Task EnviarArquivos()
    {
        var extensoesPermitidas = new string[] { ".png", ".jpg", ".jpeg", ".gif" };
        Mensagem = "";
        if(Arquivo.Anexos.Count <=0 )
        {
            Mensagem = "Nenhum arquivo selecionado !!!";
            return;
        }
        try
        {
            foreach (var arquivo in Arquivo.Anexos)
            {
                string nomeArquivo = WebUtility.HtmlEncode(arquivo.FileName);
                var arquivoExtensao = Path.GetExtension(nomeArquivo);
                var nomeArquivoSeguro = $"{Guid.NewGuid()}{arquivoExtensao}";
                if (!extensoesPermitidas.Contains(arquivoExtensao))
                {
                    Mensagem = $"Arquivo: {nomeArquivo}, é um tipo de Arquivo não permitido";
                    return;
                }
                if (arquivo.Length > tamanhoMaximoArquivo)
                {
                    Mensagem = $"Arquivo: {nomeArquivo} excede o tamanho máximo permitido.";
                    return;
                }
                var diretorioUpload = Path.Combine(env.WebRootPath, "uploads");
                if (!Directory.Exists(diretorioUpload))
                {
                    Directory.CreateDirectory(diretorioUpload);
                }
                // salva o arquivo localmente
                var path = Path.Combine(diretorioUpload, nomeArquivoSeguro);
                await using FileStream fs = new(path, FileMode.Create);
                await arquivo.CopyToAsync(fs);
                // salva imagem no banco de dados
                var arquivoDados = new ArquivoDatabase();
                arquivoDados.NomeArquivoUpload = nomeArquivoSeguro;
                await _repository.UploadArquivoDb(arquivoDados);
            }
            Arquivo = new();
            Mensagem = "Arquivo(s) enviado(s) !!!!";
        }
        catch (Exception e)
        {
            Mensagem = "Error: " + e.Message;
        }
    }
    private class ArquivoUpload
    {
        [Required(ErrorMessage ="O título é obrigatório")]
        public string Titulo { get; set; } = String.Empty;
        [Required(ErrorMessage = "A descrição é obrigatória")]
        public string Descricao { get; set; } = String.Empty;
        public IFormFileCollection? Anexos { get; set; }
    }
}

Vamos entender o código:

  1. Rota da Página: @page "/uploadssr" - Define a rota da página Blazor como "/uploadssr".
  2. Namespace: @using BlazorUploadSSR.Entities - Importa o namespace BlazorUploadSSR.Entities.
  3. Elementos HTML: Define um título e uma descrição para a página.
  4. Utiliza um formulário Blazor (<EditForm>) para lidar com a entrada do usuário.
    O modelo (Model="@Arquivo") é a instância da classe ArquivoUpload.
    OnValidSubmit="@EnviarArquivos" especifica a ação a ser executada quando o formulário é submetido.
    Usa method="post" e enctype="multipart/form-data" para permitir o envio de arquivos.
  1. Validação de Arquivos:
  2. Criação de Diretório:
  3. Salvação Local dos Arquivos:
  4. Salvação no Banco de Dados:
  5. Limpeza e Feedback:

A seguir vamos criar o componente ExibirArquivos.razor usando o seguinte código:

@page "/exibirarquivos"
@using BlazorUploadSSR.Entities
@rendermode InteractiveServer
<h3>Arquivos Armazenados</h3>
@if (arquivos != null && arquivos.Any())
{
    <div class="file-list">
        @foreach (var arquivo in arquivos)
        {
            <div class="file-item">
                <img src="\uploads\@arquivo.NomeArquivoUpload" alt="arquivo" />
                <p>@arquivo.NomeArquivoUpload</p>
                <button @onclick="() => ExcluirArquivo(arquivo.Id)">Excluir</button>
            </div>
        }
    </div>
}
else
{
    <li>Nenhum arquivo encontrado</li>
}
@code {
    private IEnumerable<ArquivoDatabase>? arquivos;
    protected override async Task OnInitializedAsync()
    {
        await CarregaImagens();
    }
    private async Task ExcluirArquivo(int arquivoId)
    {
        await _repository.DeletaArquivo(arquivoId);
        arquivos = await _repository.GetArquivos();
    }
    private async Task CarregaImagens()
    {
        arquivos = await _repository.GetArquivos();
    }
}

Este código exibe os arquivos salvos no banco de dados SQLite e permite excluir os arquivos da tabela sem contudo excluir os arquivos da pasta uploads.

Agora vamos definir um estilo vinculado a este componente criando o arquivo ExibirArquivos.razor.css com este código:

.file-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between; 
}
.file-item {
    margin: 5px;
    text-align: center;
}
img {
    max-width: 120px; /* Tamanho máximo da imagem */
    max-height: 120px;
    border: 1px solid #ccc; 
}
/*.file-item:hover {
    transform: scale(3.0); 
}*/

Executando o projeto teremos o seguinte resultado:

1- Pagina inicial

2- Enviando arquivos

3- Visualizando arquivos

Pegue o código do projeto no github : https://github.com/macoratti/BlazorUpload-Static-Server-Rendering

E estamos conversados...

"Os dias da nossa vida chegam a setenta anos, e se alguns, pela sua robustez, chegam a oitenta anos, o orgulho deles é canseira e enfado, pois cedo se corta e vamos voando."
Salmos 90:10

Referências:


José Carlos Macoratti