ASP .NET MVC 5 - Salvando e Exibindo imagens do banco de dados - I


 Neste tutorial vou mostrar como podemos enviar e salvar imagens em um banco de dados e também como exibir essas imagens em uma aplicação ASP .NET MVC 5.

Este é um tutorial prático onde vamos enviar e salvar imagens em um banco de dados SQL Server e depois vamos exibir as imagens existentes no banco de dados.

Nota: Se você esta iniciando com a ASP .NET MVC 5 leia os artigos nas referências para se inteirar dos conceitos usados neste tutorial.

Enviar uma imagem para um banco de dados não é normal, visto que armazenar isso pode acarretar diversos problemas. Assim, antes de tomar a decisão de usar essa abordagem, você deve verificar se realmente ela vale a pena. Vejamos alguns dos problemas que ela pode trazer:

Dentre as vantagens em usar essa abordagem temos :

Ao usar essa abordagem, outro detalhe a considerar é qual o melhor tipo de dados usar na definição do campo para armazenar a imagem.

No caso do SQL Server, o banco de dados que iremos usar, temos que ele fornece um tipo de dados especial apropriado para um grande volume de dados que é o Binary Large Objects.

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

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

LOB Type Tipo de dadaos SQL  Server Tamanho Máximo 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.

BLOB varbinary(MAX)
Image(*)
2.147.483.647
CLOB varchar(MAX)
Text(*)
2.147.483.647
CLOB - Unicode nvarchar(MAX)
NText(*)
1.073.741.823
dados XML xml 2.147.483.647

Atualmente é mais aconselhável é usar os tipos :  nvarchar(max), varchar(max) e varbinary(max). Para tratar imagem o tipo indicado seria varbinary(max).

Então vamos ao trabalho...

Recursos Usados:

Criando o banco de dados e as tabelas

Para criar o banco de dados e as tabelas eu vou usar o SQL Server Management Studio 2012 e o SQL Server 2012 Express mas você pode usar qualquer outra versão superior a essa.

1- Crie o banco de dados chamado Cadastro.mdf

USE [master]
GO
CREATE DATABASE Cadastro

 

2- Crie a tabela Categorias

USE Estudo
GO
CREATE TABLE dbo.Categorias(
	CategoriaId int IDENTITY(1,1) NOT NULL,
	CategoriaNome nvarchar(100) NOT NULL,
 CONSTRAINT [PK_Categorias] PRIMARY KEY CLUSTERED 
(
	[CategoriaId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

3- Crie a tabela Produtos

USE Estudo
GO
CREATE TABLE dbo.Produtos(
	ProdutoId int IDENTITY(1,1) NOT NULL,
	Nome nvarchar(100) NOT NULL,
	Descricao nvarchar(150) NOT NULL,
	CategoriaId int NOT NULL,
	Estoque int NULL,
	Preco decimal(18, 2) NOT NULL,
	Imagem varbinary(max) NULL,
CONSTRAINT [PK_Produtos] PRIMARY KEY CLUSTERED 
(
	[ProdutoId] 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]

3- Defina o relacionamento entre as tabelas Produtos e Categorias

4- Cadastre algumas categorias e alguns produtos para serem exibidos: (neste caso as imagens serão null)

Criando o projeto ASP .NET MVC 5

Abra o VS 2017 Community e crie um projeto usando o template Web-> ASP .NET Web Application(.NET Framework) e informe o nome Mvc_DBImagem;

A seguir marque o template MVC sem autenticação e clique em OK;

Será criado um projeto com toda a estrutura pronta para ser usada.

Incluindo a referência ao Entity Framework

Vamos incluir o pacote do EF 6.2 no projeto via Nuget: (Menu Tools->Manage Nuget Packages for Solution)

Clique em Browse, selecione o pacote e clique em Install:

Criando o modelo de domínio e a classe de Contexto

Na pasta Models do projeto vamos criar as classes Categoria e Produto que representam o nosso modelo de domínio e que serão mapeadas para as tabelas Categorias e Produtos via EF.

Vamos usar o recurso Data Annotations para definir a validação e formatação de dados e mensagens de erros.

1- Classe Produto

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Mvc_Imagem.Models
{
    [Table("Produtos")]
    public class Produto
    {
        public int ProdutoId { get; set; }

        [Required(ErrorMessage = "O nome do produto é obrigatório", AllowEmptyStrings = false)]
        public string Nome { get; set; }
        [Required(ErrorMessage = "A descrição do produto é obrigatória", AllowEmptyStrings = false)]
        public string Descricao { get; set; }
        [Required(ErrorMessage = "Informe o estoque do produto")]
        public int Estoque { get; set; }
        [Required(ErrorMessage = "Informe o preço do produto", AllowEmptyStrings = false)]
        [DisplayFormat(DataFormatString = "{0:C2}", ApplyFormatInEditMode = true)]
        public decimal Preco { get; set; }
        public byte[] Imagem { get; set; }
        public int CategoriaId { get; set; }
        public virtual Categoria Categoria { get; set; }
    }
}

2- Classe Categoria

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Mvc_Imagem.Models
{
    [Table("Categorias")]
    public class Categoria
    {
        public int CategoriaId { get; set; }
        [Display(Name = "Nome da Categoria")]
        public String CategoriaNome { get; set; }

        public List<Produto> Produtos { get; set; }
    }
}

3- Classe de contexto ProdutoDbContext

using System.Data.Entity;
namespace Mvc_Imagem.Models
{
    public class ProdutoDbContext : DbContext
    {
        public DbSet<Produto> Produtos { get; set; }
        public DbSet<Categoria> Categorias { get; set; }
    }
}

A classe de contexto indica ao EF o mapeamento existente e permite o acesso às tabelas do banco de dados.

Definindo a string de conexão no arquivo web.config

Precisamos definir a string de conexão no arquivo web.config para informar ao EF onde esta no banco de dados.

Abra o arquivo Web.Config do projeto e inclua o código abaixo:

...
<connectionStrings>
   <add name="ProdutoDbContext" connectionString="Data Source=.\;Initial Catalog=Cadastro;Integrated Security=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
...

Observe que o nome da string de conexão é o mesmo que o nome da nossa classe de Contexto: ProdutoDbContext.

Criando a view Modelo ProdutoViewModel

Para realizar a inclusão de produtos vamos definir uma view model representada pela classe ProdutoViewModel na pasta Models:

using System;
using System.ComponentModel.DataAnnotations;
using System.Web;
namespace Mvc_Imagem.Models
{
    public class ProdutoViewModel
    {
        public Int32 ProdutoId { get; set; }
        [Required(ErrorMessage = "O nome do produto é obrigatório", AllowEmptyStrings = false)]
        [Display(Name = "Nome do Produto")]
        public String Nome { get; set; }
        [Required(ErrorMessage = "A descrição do produto é obrigatória", AllowEmptyStrings = false)]
        [Display(Name = "Descrição do Produto")]
        public String Descricao { get; set; }
        [Required(ErrorMessage = "Informe o preço do produto", AllowEmptyStrings = false)]
        [Display(Name = "Preço")]
        public Decimal Preco { get; set; }
        [Required(ErrorMessage = "Selecione uma categoria", AllowEmptyStrings = false)]
        [Display(Name = "Categoria")]
        public Int32 CategoriaId { get; set; }
        [Required]
        [DataType(DataType.Upload)]
        [Display(Name = "Imagem")]
        public HttpPostedFileBase ImageUpload { get; set; }      
    }
}

Vamos usar essa view model apenas na view Create.

Ajustando o arquivo de leiaute: _Layout.cshtml e a view Index

Vamos ajustar o arquivo de leiaute usado pela aplicação para definir um menu com opções para acessar Produtos.

Abra o arquivo _Layout.cshtml na pasta /Views/Shared e inclua o código destacado em azul:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - Cadastro de Produtos</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("Cadastro de Produtos", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("Produtos","Index","Produtos")</li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - Macoratti. net</p>
        </footer>
    </div>
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

Abra agora o arquivo Index.cshtml na pasta Views/Home e altere o código deste arquivo conforme abaixo:

@{
    ViewBag.Title = "Home Page";
}
<div class="container">
    <div class="jumbotron">
        <h2>Cadastro de Produtos</h2>
    </div>
 </div>

Temos um jumbotron do bootstrap.

Criando o controlador ProdutosController

Vamos criar o controlador ProdutosController na pasta Controllers onde vamos definir os métodos Action para exibir e realizar as operações CRUD.

Clique com o botão direito sobre a pasta Controllers e a seguir clique em Add e depois em Controller;

Selecione a opção MVC 5 Controller Empty do Scaffolding  clique em Add e informe o nome ProdutosController e clique em Add;

Inclua o código abaixo neste controlador:

using Mvc_Imagem.Models;
using System;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Web.Mvc;
namespace Mvc_Imagem.Controllers
{
    public class ProdutosController : Controller
    {
        ProdutoDbContext db;
        public ProdutosController()
        {
            db = new ProdutoDbContext();
        }
        // GET: Produtos
        public ActionResult Index()
        {
            var produtos = db.Produtos.ToList();
            return View(produtos);
        }
     }
}

Neste código criamos uma instância do contexto e obtemos todos os produtos no método Action Index repassando para a view Index.cshml

Criando a View para exibir os produtos

Vamos criar a View Index para exibir os produtos cadastrados.

Clique com o botão direito sobre o método Action Index do controlador ProdutosController e a seguir clique em Add View;

Aceite os valores definidos na janela Add View e clique em Add:

A seguir inclua o código abaixo :

 @model IEnumerable<Mvc_Imagem.Models.Produto>
@{
    ViewBag.Title = "Index";
}
<h2>Produtos</h2>
<p>
    @Html.ActionLink("Incluir Novo Produto","Create","Produtos", new { @class = "btn btn-success" })
</p>
<table class="table">
    <tr>
        <th>@Html.DisplayNameFor(model => model.Categoria.CategoriaNome)</th>
        <th>@Html.DisplayNameFor(model => model.Nome) </th>
        <th>@Html.DisplayNameFor(model => model.Descricao)</th>
        <th>@Html.DisplayNameFor(model => model.Preco)</th>
        <th>@Html.DisplayNameFor(model => model.Estoque)</th>
        <th>@Html.DisplayNameFor(model => model.Imagem)</th>
        <th></th>
    </tr>
@foreach (var item in Model) {
    <tr>
        <td>@Html.DisplayFor(modelItem => item.Categoria.CategoriaNome)</td>
        <td>@Html.DisplayFor(modelItem => item.Nome)</td>
        <td>@Html.DisplayFor(modelItem => item.Descricao)</td>
        <td>@Html.DisplayFor(modelItem => item.Preco)</td>
        <td>@Html.DisplayFor(modelItem => item.Estoque)</td>
         @if (item.Imagem != null)
         {
           <td>
                 @{
                     var base64 = Convert.ToBase64String(item.Imagem);
                     var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
                  }
                 <img src='@imgSrc' style="max-width:100px; max-height:100px;" />
            </td>
           }
        <td>
            @Html.ActionLink("Editar", "Edit", new { id = item.ProdutoId }) |
            @Html.ActionLink("Detalhes", "Details", new { id = item.ProdutoId }) |
            @Html.ActionLink("Deletar", "Delete", new { id = item.ProdutoId })
        </td>
    </tr>
}
</table>

No código desta View usamos o model Produto e temos uma view fortemente tipada onde exibimos os dados dos produtos.

Para exibir a imagem do produto fizemos o seguinte:

O botão para incluir produtos actiona o método Action Create do controlador Produtos que iremos criar a seguir.

Agora é só alegria...

Executando o projeto teremos o seguinte resultado:

1- A página inicial exibida pela view Index

2 - Ao clicar no link Produtos teremos a exibição dos produtos

Nesta página temos o botão para incluir um produto e os links para editar, ver detalhes e deletar produtos.

Na próxima parte do artigo vamos implementar a inclusão de produtos e suas imagens.

"E Jesus, tendo ouvido isto, disse-lhes: Os sãos não necessitam de médico, mas, sim, os que estão doentes; eu não vim chamar os justos, mas, sim, os pecadores ao arrependimento. "
Marcos 2:17

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti