Blazor - Upload de arquivos (InteractiveServer) - II


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

Continuando o artigo anterior vamos criar o projeto Blazor e implementar o Upload de arquivos.

Criando o projeto

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

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.

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 ArquivoUpload:

public class ArquivoUpload
{
    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));

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": "*"
}

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;
    }
}

Criando o serviço de Upload

Para realizar o upload de arquivos vamos criar um serviço de upload criando uma pasta Services no projeto e nesta pasta criando a interface IUploadService e UploadService:

IUploadService

public interface IUploadService
{
    Task<(int, string)> ArquivoUploadAsync(IBrowserFile arquivo, 
                                           int tamanhoMaximoArquivo, 
                                           string[] extensoesPermitidas);
}

UploadService

public class UploadService : IUploadService
{
    private readonly IWebHostEnvironment _environment;
    private readonly ILogger<UploadService> logger;
    public UploadService(IWebHostEnvironment environment, 
                         ILogger<UploadService> logger)
    {
        _environment = environment;
        this.logger = logger;
    }
    public async Task<(int, string)> ArquivoUploadAsync(IBrowserFile arquivo, 
                                                        int tamanhoMaximoPermitido, 
                                                        string[] extensoesPermitidas)
    {
        var diretorioUpload = Path.Combine(_environment.WebRootPath, "uploads");
        if (!Directory.Exists(diretorioUpload))
        {
            Directory.CreateDirectory(diretorioUpload);
        }
        if (arquivo.Size > tamanhoMaximoPermitido)
        {
            var mensagem = $"Arquivo: {arquivo.Name} excede o tamanho máximo permitido.";
            logger.LogInformation(mensagem);
            return (0, mensagem);
        }
        var arquivoExtensao = Path.GetExtension(arquivo.Name);
        if (!extensoesPermitidas.Contains(arquivoExtensao))
        {
            var mensagem = $"Arquivo: {arquivo.Name}, tipo de Arquivo não permitido";
            logger.LogInformation(mensagem);
            return (0, mensagem);
        }
        //altera o nome do arquivo
        var nomeArquivoSeguro = $"{Guid.NewGuid()}{arquivoExtensao}";
        //obtem o caminho do arquivo em wwwroot 
        var path = Path.Combine(diretorioUpload, nomeArquivoSeguro);
        //cria o arquivo
        await using var fs = new FileStream(path, FileMode.Create);
        // lê e copia paraa memoria
        await arquivo.OpenReadStream(tamanhoMaximoPermitido).CopyToAsync(fs);
        return (1, nomeArquivoSeguro);
    }
}

Agora não podemos esquecer de registrar os serviços criados 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<IUploadService, UploadService>();
builder.Services.AddTransient<IUploadRepository, UploadRepository>();

var app = builder.Build();
...

app.Run();

Para poder criar o banco de dados SQLite sem aplicar o Migrations vamos criar o método CreateDatabase na classe Program:

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 para fazer o upload de arquivos e para exibir os nomes e imagens dos arquivos enviados.

No próximo artigo vamos concluir a implementação....

"(Disse Jesus) Estai em mim, e eu em vós; como a vara de si mesma não pode dar fruto, se não estiver na videira, assim também vós, se não estiverdes em mim."
João 15:4

Referências:


José Carlos Macoratti