.NET

2 ago, 2018

ASP .NET Core MVC – Salvando e exibindo imagens a partir de um banco de dados SQL Server

Publicidade

Neste artigo eu vou mostrar como salvar imagens e exibi-las em um banco de dados SQL Server a partir de uma aplicação ASP .NET Core MVC.

Quando você precisa lidar com dados binários como imagens, a maneira mais usada para armazenar esses dados é no sistema de arquivos do sistema operacional, ou seja, do Windows; assim, você armazena os dados binários como um arquivo no disco local.

A outra abordagem é armazenar os dados binários diretamente em um banco de dados como o SQL Server que será o nosso caso.

Cada opção possui seus prós e seus contras. A seguir, eu relaciono alguns motivos que podem justificar cada opção:

1 – Armazenar os dados binários como um arquivo no disco local é uma boa opção se:

  • Você não possuir espaço suficiente no SQL Server;
  • Os dados binários serão usados por terceiros;
  • Você deseja usar um editor de arquivos de sistema para manipular os dados;

O grande problema com esta solução, é que os dados estarão fora do banco de dados e podem perder a sincronia com os demais dados em operações de exclusão, atualização, inclusão e transferência de dados. Outro fator a considerar é que o backup deverá ser feito separado.

2 – Armazenar os dados binários diretamente no banco de dados SQL Server possui as seguinte vantagens:

Os dados binários estão sempre junto com os demais dados e não há perigo de perda de dados;
Os dados binários são transferidos junto com os demais dados sem a necessidade de qualquer procedimento manual;

A cópia de segurança dos dados é feita de uma vez em um único processo;

Usando o tipo de dados varbinary do SQL Server podemos armazenar arquivos com até 2GB de tamanho.

Para estes casos específicos, o SQL Server fornece um tipo de dados especial, apropriado para um grande volume de dados, e neste artigo eu vou mostrar como podemos ler e gravar BLOBs – Binary Large Objects no SQL Server em uma aplicação ASP .NET Core MVC.

Nota: BLOB é um acrônimo para binary large object , uma coleção de dados binários armazenados em uma identidade única no SQL Server.

Esses tipos de dados, Large Objects, podem ser classificados em CLOBsCharacter Large Objects ou BLOBsBinary Large Objects, e o SQL Server possui um tipo diferente de dados para cada um destes objetos. Vejamos na tabela abaixo os tipos de dados que podemos usar neste caso:

Os tipos de dados (*) Text, NText e Image já existiam em versões anteriores do SQL Server. É recomendado que você use os novos tipos de dados: varchar(MAX), nvarchar(MAX) e varbinary(MAX).

Observando a tabela, vemos que o tipo de dados varbinary(MAX) é o que permite tratar com imagens ou Large Binary Data. No exemplo deste artigo eu vou partir de um banco de dados existente no SQL server chamado Estudo e de uma tabela Imagens com a seguinte estrutura:

USE Estudo
GO
CREATE TABLE dbo.Imagens(
	Id int IDENTITY(1,1) NOT NULL,
	Nome nvarchar(150) NOT NULL,
	ContentType nvarchar(50) NOT NULL,
	Dados varbinary(max) NULL,
 CONSTRAINT [PK_Imagens] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

Ao lado vemos o script SQL para gerar a tabela. Note que o tipo de dados para a imagem (Dados) é varbinary(max).

Recursos usados

Criando o projeto no VS 2017

Abra o VS 2017 Community e crie um novo projeto ASP .NET Core usando o template WebApplication (Model-View-Controller).

  • Create New Project;
  • Visual C# -> Web -> ASP .NET Core Web Application;
  • Informe o nome AspCore_EnviaExibeImagem
  • Selecione o template Web Application(Model-View-Controller), marque ASP .NET Core 2.0;

Após criar o projeto, já podemos definir o modelo e o contexto usados na nossa aplicação.

Criando o modelo de domínio e o contexto

Vamos criar a classe Imagem que representa o nosso modelo de domínio na pasta Models:

public class Imagem
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public byte[] Dados { get; set; }
        public string ContentType { get; set; }
    }

A seguir, criaremos a classe de contexto ImagemDbContext que herda de DbContext na pasta Models:

using Microsoft.EntityFrameworkCore;
namespace AspNetCore_EnviaExibeImagem.Models
{
    public class ImagemDbContext : DbContext
    {
        public ImagemDbContext(DbContextOptions<ImagemDbContext> options) : base(options)
        { }
        public DbSet<Imagem> Imagens { get; set; }
    }
}

Definindo a string de conexão no arquivo appsettings.json

Precisamos informar ao Entity Framework qual o caminho do banco de dados que vamos acessar. Para isso, vamos incluir a string de conexão no arquivo appsettings.json:

{
  "ConnectionStrings": {
    "ImagemContext": "Data Source=Macoratti;Initial Catalog=Estudo;Integrated Security=True"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

Registrandao o contexto e configurando o serviço em Startup

Vamos registrar o nosso contexto no método ConfigureServices da classe Startup definindo também a utilização do provedor do SQL Server e obtendo a string de conexão definida no arquivo appsettings.json:

....
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ImagemDbContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("ImagemContext")));
            services.AddMvc();
        }
...

Agora já temos tudo pronto para acessar o banco de dados. Vamos criar nosso controlador.

Criando o controlador ImagensController e view Index

Clique com o botão direito do mouse sobre a pasta Controllers, e a seguir clique em “Add“-> “Controller” e escolha a opção “MVC Controller Empty” e clique “Add“;

Informe o nome ImagensController e a seguir defina o código abaixo neste controlador:

using AspNetCore_EnviaExibeImagem.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace AspNetCore_EnviaExibeImagem.Controllers
{
    public class ImagensController : Controller
    {
        ImagemDbContext _context;
        public ImagensController(ImagemDbContext contexto)
        {
            _context = contexto;
        }
        [HttpGet]
        public IActionResult Index()
        {
            List<int> imagens = _context.Imagens.Select(m => m.Id).ToList();
            return View(imagens);
        }
        [HttpPost]
        public IActionResult UploadImagem(IList<IFormFile> arquivos)
        {
            IFormFile imagemEnviada = arquivos.FirstOrDefault();
            if (imagemEnviada != null || imagemEnviada.ContentType.ToLower().StartsWith("image/"))
            {
                MemoryStream ms = new MemoryStream();
                imagemEnviada.OpenReadStream().CopyTo(ms);
                Imagem imagemEntity = new Imagem()
                {
                    Nome = imagemEnviada.Name,
                    Dados = ms.ToArray(),
                    ContentType = imagemEnviada.ContentType
                };
                _context.Imagens.Add(imagemEntity);
                _context.SaveChanges();
            }
            return RedirectToAction("Index");
        }
        [HttpGet]
        public FileStreamResult VerImagem(int id)
        {
            Imagem imagem = _context.Imagens.FirstOrDefault(m => m.Id == id);
            MemoryStream ms = new MemoryStream(imagem.Dados);
            return new FileStreamResult(ms, imagem.ContentType);
        }
    }
}

Para criar a view, clique com o botão direito sobre o método Action Index e a seguir clique em Add View;

Aceite as definições conforme mostra a figura abaixo e clique em Add;

A seguir vamos definir o código na view para gerar o nosso arquivo PDF.

Definindo o código na view Index

Aqui colocamos o código para enviar a imagem e a seguir exibir a imagem enviada:

@model IList<int>
<h2>Enviar Imagens</h2>
<hr>
<form action="/Imagens/UploadImagem" enctype="multipart/form-data" method="post">
    <input type="file" name="arquivos" />
    <br />
    <button>Enviar Imagem</button>
</form>
<br />
@foreach (var item in Model)
{
   <img src="/Imagens/VerImagem/@item" />
}

Alterando o arquivo de layout

Vamos agora abrir o arquivo _Layout.cshml na pasta /Views/Shared e incluir o código em azul mostrado abaixo, substituindo o código original:

...
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
<li><a asp-area="" asp-controller="Imagens" asp-action="Index">Enviar Imagens</a></li>
</ul>
</div>

...

Agora vamos alterar o código da view Index.cshtml da pasta Views/Home:

@{
    ViewData["Title"] = "Home Page";
}
<br />
<div>
    <h3>Gerar arquivos PDF</h3>
</div>

Fizemos isso somente para exibir um link para Enviar Imagens na página inicial.

Agora é só alegria!

Executando projeto iremos obter o seguinte resultado:

Vemos que podemos enviar imagens que são imediatamente exibidas na mesma página.

Pegue o código do projeto aqui: AspNetCore_EnviaExibeImagem.zip.