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 CLOBs – Character Large Objects ou BLOBs – Binary 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.