Blazor - Upload de arquivos (Static Server) - I


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

A Static Server Rendering ou renderização estática do lado do servidor executa o código no lado do servidor, como as Razor Pages e as aplicações MVC fazem.

A resposta completa é então enviada ao navegador para exibição ao visitante e não há mais interação entre o servidor e o cliente até que o navegador faça uma nova requisição HTTP.

Para que a renderização estática funcione, o mínimo necessário são duas chamadas para AddRazorComponents e MapRazorComponents<App>() configuradas na aplicação Blazor.

Este é o modo padrão de renderização quando você não definir outro modo de renderização.

Funciona assim:

  1. O navegador envia o request ao servidor;
  2. O servidor recebe e encaminha o request roteando o para um componente adequado;
  3. O componente é renderizado e retornado o HTML resultante;

A renderização estática do lado do servidor é útil onde os componentes não precisam ser interativos.

Esse modo de renderização é simples e rápido e não exige que o componente viva por mais tempo do que a resposta HTML leva para ser renderizada e devolvida

Destacando que o modo de renderização Static Server Rendering é o modo de renderização padrão para aplicações Blazor no .NET 8 (sem interatividade)

Vejamos agora alguns conceitos importantes relacionados com este modo de renderização :

Criando o projeto

Vamos criar um novo projeto usando o template Blazor Web App usando o VS 2022 com o nome BlazorUploadSSR :

Definindo as seguintes configurações:

Neste projeto além de enviar arquivos para o servidor vamos também salvar o caminho do arquivo enviado em um banco de dados SQLite e a seguir usar essa informação para exibir as imagens.

Para isso vamos incluir no projeto o pacote nuget:  Microsoft.EntityFrameworkCore.Sqlite

Vamos também criar na pasta wwwroot do projeto a pasta uploads para receber os arquivos enviados.

Para representar o arquivo que iremos salvar vamos criar uma pasta Entities e nesta pasta vamos cria a classe ArquivoDatabase:

public class ArquivoDatabase
{
    public int Id { get; set; }
    public string? NomeArquivoUpload { get; set; }
}

Vamos criar a pasta Context no projeto e nesta pasta criar a classe de contexto AppDbContext:

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions options) : base(options)
    { }

    public DbSet<ArquivoUpload> ArquivosUploads { get; set; }
}

Aqui definimos o mapeamento ORM da entidade para a tabela no SQLite.

A seguir na classe Program vamos registrar o contexto no contêiner DI :

var connectionString = builder.Configuration.GetConnectionString("Sqlite");

builder.Services.AddDbContext<AppDbContext>(opt => 
                               opt.UseSqlite(connectionString));

Neste código a string de conexão esta sendo obtida do arquivo appsettings.json:

{
  "ConnectionStrings": {
    "Sqlite": "data source=ArquivosDB"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Aqui definimos que o nome do banco a ser criado será ArquivosDB.

Criando o Repositório

Para poder realizar operações com o banco de dados SQLite vamos criar uma pasta Repositories no projeto e nesta pasta vamos criar a interface IUploadRepository e sua implementação que será feita na classe concreta UploadRepository.

Aqui estamos usando o seguinte código:

IUploadRepository

public interface IUploadRepository
{
    Task UploadArquivoDb(ArquivoUpload arquivo);
    Task<IEnumerable<ArquivoUpload>> GetArquivos();
    Task<ArquivoUpload> GetArquivo(int id);
    Task DeletaArquivo(int id);
}

UploadRepository

public class UploadRepository : IUploadRepository
{
    private readonly AppDbContext _context;
    public UploadRepository(AppDbContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }
    public async Task UploadArquivoDb(ArquivoUpload arquivo)
    {
        _context.ArquivosUploads.Add(arquivo);
        await _context.SaveChangesAsync();
    }
    public async Task<IEnumerable<ArquivoUpload>> GetArquivos()
    {
        return await _context.ArquivosUploads.ToListAsync();
    }
    public async Task DeletaArquivo(int id)
    {
        var arquivo = await GetArquivo(id);
        if (arquivo is not null)
        {
            _context.Remove(arquivo);
            await _context.SaveChangesAsync();
        }
    }
    public async Task<ArquivoUpload> GetArquivo(int id)
    {
        var arquivo = await _context.ArquivosUploads.FirstOrDefaultAsync(a => a.Id == id);
        return arquivo;
    }
}

Agora não podemos esquecer de registrar o serviço do contexto e do serviço do repositório no contêiner DI:

var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();
var connectionString = builder.Configuration.GetConnectionString("Sqlite");
builder.Services.AddDbContext<AppDbContext>(opt =>
                                  opt.UseSqlite(connectionString));

builder.Services.AddTransient<IUploadRepository, UploadRepository>();
...

Para poder criar o banco de dados SQLite sem aplicar o Migrations vamos criar o método CreateDatabase na classe Program conforme o código a seguir:

var builder = WebApplication.CreateBuilder(args);
...

var app = builder.Build();

CreateDatabase(app);
...

app.Run();
static void CreateDatabase(WebApplication app)
{
    var serviceScope = app.Services.CreateScope();
    var dataContext = serviceScope.ServiceProvider.GetService<AppDbContext>();
    dataContext?.Database.EnsureCreated();
}

Com isso na primeira execução do projeto, o banco de dados ArquivosDB será criado.

Com isso temos tudo pronto para criar os componentes Blazor e faremos isso no próximo artigo.

"Na verdade, na verdade vos digo que quem ouve a minha palavra, e crê naquele que me enviou, tem a vida eterna, e não entrará em condenação, mas passou da morte para a vida."
João 5:24

Referências:


José Carlos Macoratti