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. | 
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:
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:
C# - Tasks x Threads. Qual a diferença
DateTime - Macoratti.net
Null o que é isso ? - Macoratti.net
Formatação de data e hora para uma cultura ...
C# - Calculando a diferença entre duas datas
NET - Padrão de Projeto - Null Object Pattern
C# - Fundamentos : Definindo DateTime como Null ...
C# - Os tipos Nullable (Tipos Anuláveis)