C# - Armazenando Imagens no SQL Server


Se você espiar as referências deste artigo vai concluir que eu já publiquei muitos artigos sobre o tratamento de imagens com banco de dados.(A maioria deles usando a linguagem VB .NET e com aplicações ASP .NET.)

Neste artigo eu vou mostrar como armazenar imagens em um banco de dados SQL Server usando a linguagem C# em uma aplicação Windows Forms usando o Visual C# 2010 Express Edition que permitirá selecionar e armazenar imagens do tipo .GIF ou .JPG em banco de dados SQL Server.

Serão armazenados o caminho e nome da imagem e a imagem no formato binário na tabela Imagens do banco de dados JcmSoft.mdf do SQL Server 2005.

Conceitos básicos sobre dados binários

Dados binários podem se apresentar em uma variedade de formas como: documentos do word, arquivos PDF, Fotos , imagens , etc.

A maneira mais usada para armazenar dados binários é no sistema de arquivos de Windows, ou seja, armazenar os dados binários como um arquivo no disco local e a outra , a que irei mostrar neste artigo é armazenar os dados binários diretamente no SQL Server.

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 :

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:

Usando o tipo de dados varbinary  do SQL Server podemos armazenar arquivos com até 2GB de tamanho, e, neste exemplo eu vou criar uma aplicação Windows Forms para armazenar imagens do tipo .GIF ou .JPG que serão armazenadas diretamente no banco de dados JcmSoft.mdf , mais precisamente na tabela Imagens

Para estes casos específicos o SQL Server fornece um tipo de dados especial apropriados para um grande volume de dados e neste artigo eu vou mostrar como podemos ler e gravar BLOBs - Binary Large Objects usando o SQL Server e ADO .NET.

 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

Criando o Banco de dados JcmSoft.mdf e definindo a string de conexão

Para criar o banco de dados você pode usar o SQL Server Management Studio e selecionar a opção File -> New -> Data Base Engine Query e a seguir usar o script SQL abaixo para criar o banco de dados na janela New Query clicando na opção Execute:

CREATE DATABASE [JcmSoft] ON PRIMARY
(
NAME = N'JcmSoft', FILENAME = N'c:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\DATA\JcmSoft.mdf' , SIZE = 4352KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
LOG ON
(
NAME = N'JcmSoft_log', FILENAME = N'c:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\DATA\JcmSoft_log.LDF' , SIZE = 1856KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO

A tabela Imagens  usada para armazenar os arquivos de imagem será criada pelo programa C#.

A string de conexão usada para fazer a conexão com o banco de dados será armazenada no arquivo App.Config conforme mostrado abaixo na figura 1.0:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <clear />
    <add name="conexaoSQLServer"
     providerName="System.Data.SqlClient"
     connectionString="server =.\SQLEXPRESS;integrated security = true;database=JcmSoft;" />
  </connectionStrings>
</configuration>
Figura 1.0

Criando o projeto Windows Forms

Abra o Visual C# 2010 Express Edition e no menu File selecione New Project e a seguir o template Windows Forms Application definindo o nome InserindoImagens e clicando no botão OK;

A seguir vamos incluir o arquivo de configuração App.Config. Clique no menu Project -> Add New Item e a seguir selecione o template Application Configuration File aceitando o nome padrão App.Config e clicando em Add;

Depois é só copiar o código da Figura 1.0 descrito anteriormente.

Vamos agora incluir uma classe no projeto selecionando o menu Project -> Add Class e selecionando o template Class, informando o nome CarregaImagem.cs e clicando no botão Add;

Vamos definir o código da classe CarregaImagem conforme mostrado a seguir:

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Configuration;
namespace Macoratti
{
    public class CarregaImagem
    {
        int ImagemTamanhoMaximo = 1200000;
        SqlConnection conn = null;
        SqlCommand cmd = null;

        static string GetStringConexaoPorNome(string nome)
        {
            //define a variável
            string retorno = null;
            // Procura pelo nome na seção  connectionStrings do app.config
            ConnectionStringSettings settings =
                ConfigurationManager.ConnectionStrings[nome];
            // se achou retorna a string de conexçao
            if (settings != null)
                retorno = settings.ConnectionString;
            return retorno;
        }
        /// <summary>
        /// Abre uma conexão SQL Server
        /// </summary>
        public void AbrirConexao()
        {
            try
            {
                // cria a conexão
                conn = new SqlConnection(GetStringConexaoPorNome("conexaoSQLServer"));
                // abre a conexão
                conn.Open();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Fecha a conexão
        /// </summary>
        public void FecharConexao()
        {
            try
            {
                // fecha a conexão
                conn.Close();
            }            
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Cria um objeto command
        /// </summary>
        public void CriarComando()
        {
            try
            {
                cmd = new SqlCommand();
                cmd.Connection = conn;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Executa o command
        /// </summary>
        /// <param name="cmdText"></param>
        public void ExecutarComando(string cmdTexto)
        {
            try
            {
                int cmdResultado;
                cmd.CommandText = cmdTexto;
                cmdResultado = cmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Cria uma tabela para as imagens
        /// </summary>
        public void CriarTabelaImagem()
        {
            //verifica se a tabela Imagens existe antes de criar
            try
            {
                if (!TabelaExiste("Imagens", GetStringConexaoPorNome("conexaoSQLServer")))
                    ExecutarComando(@"create table Imagens(nome nvarchar(100),imagem varbinary(max))");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Prepara a inclusão das imagens na tabela
        /// </summary>
        public void PrepararInclusaoImagem()
        {
            //cria o comando sql para incluir as imagens na tabela
            try
            {
                cmd.CommandText = @"insert into Imagens values (@nome, @imagem)";
                cmd.Parameters.Add("@nome", SqlDbType.NVarChar, 100);
                cmd.Parameters.Add("@imagem", SqlDbType.Image, 1200000);
                cmd.Prepare();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Executa a inclusão da imagem na tabela
        /// </summary>
        /// <param name="imageFileNumber"></param>
        public void ExecutarInclusaoImagem(string nomeArquivo, string caminhoArquivo)
        {
            try
            {
                byte[] imagemDados = null;
                imagemDados = CarregarArquivoImagem(nomeArquivo, caminhoArquivo, ImagemTamanhoMaximo);
                cmd.Parameters["@nome"].Value = caminhoArquivo + "\\" + nomeArquivo;
                cmd.Parameters["@imagem"].Value = imagemDados;
                ExecutarComando(cmd.CommandText);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        ///  carrega a imagem 
        /// </summary>
        /// <param name="nomeArquivo"></param>
        /// <param name="caminhoArquivo"></param>
        /// <param name="ImagemTamanhoMaximo"></param>
        /// <returns></returns>
        byte[] CarregarArquivoImagem(string nomeArquivo, string caminhoArquivo, int ImagemTamanhoMaximo)
        {
            try
            {
                byte[] imagemBytes = null;
                string caminhoCompletoImagem = caminhoArquivo + "\\" + nomeArquivo;
                FileStream fs = new FileStream(caminhoCompletoImagem, FileMode.Open, FileAccess.Read);
                BinaryReader br = new BinaryReader(fs);
                imagemBytes = br.ReadBytes(ImagemTamanhoMaximo);
                return imagemBytes;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Verifica se a tabela ja existe no SQL Server
        /// </summary>
        /// <param name="NomeTabela"></param>
        /// <param name="strConnection"></param>
        /// <returns></returns>
        public bool TabelaExiste(string NomeTabela, string strConnection)
        {
            using (SqlConnection conexaoSQL = new SqlConnection(strConnection))
            {
                string verificaTabela =
                   String.Format(
                      "IF OBJECT_ID('{0}', 'U') IS NOT NULL SELECT 'true' ELSE SELECT 'false'",
                      NomeTabela);
                SqlCommand command = new SqlCommand(verificaTabela, conexaoSQL);
                command.CommandType = CommandType.Text;
                try
                {
                    conexaoSQL.Open();
                    Boolean retorno = Convert.ToBoolean(command.ExecuteScalar());
                    return retorno;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
   }
}

Embora a classe esteja documentada vou dar um resumo sobre os métodos da classe:

Na classe CarregaImagem temos os seguintes métodos:

Destaques da classe CarregaImagem:

1- O método TabelaExiste verifica se uma tabela existe no SQL Server e utiliza o seguinte comando SQL:

    IF OBJECT_ID('{0}', 'U') IS NOT NULL SELECT 'true' ELSE SELECT 'false

2- O método CriarTabelaImagem utiliza uma instrução SQL - Create Table - para criar a tabela no SQL Server com dois campos: nome e imagem;

   ExecutarComando(@"create table Imagens(nome nvarchar(100),imagem varbinary(max))");

Definindo a interface da aplicação

Vamos usar o formulário form1.cs do projeto InserindoImagens e incluindo no mesmo os seguintes controles a partir da ToolBox:

Conforme o leiaute da figura abaixo:

Nota: No VB.NET não existe mais o controle Image . Temos somente o controle PictureBox que agora suporta também arquivos GIF animados. Muitas de suas propriedades mudaram e outras não existem mais. Se você quiser consumir menos recursos pode sobrepor o evento OnPaint do formulário e usar o método DrawImage para exibir uma imagem.

O código do formulário irá tratar o evento Click das opções Abrir e Sair do menu e o evento Click do botão para inserir a imagem no banco de dados.

O usuário deverá selecionar a opção abrir para que seja exibida a caixa de diálogo onde um arquivo no formato .gif ou .jpg poderá ser escolhido;

Após esta seleção a imagem será exibida no controle PictureBox para visualização;

Ao clicar no botão de comando a imagem será inserida no banco de dados através da utilização dos métodos da classe CarregaImagem definida no projeto.

O código do formulário é o seguinte:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.IO;
namespace Macoratti
{
    public partial class Form1 : Form
    {
        string caminhoNomeArquivoSelecionado = null;
        public Form1()
        {
            InitializeComponent();
        }
        private void btnInserir_Click(object sender, EventArgs e)
        {
            CarregaImagem oImagem = new CarregaImagem();
            try
            {
                // abre a conexão
                oImagem.AbrirConexao();
                // cria um comando 
                oImagem.CriarComando();
                // cria uma tabela se não existir
                oImagem.CriarTabelaImagem();
                // prepara a inclusão da imagem
                oImagem.PrepararInclusaoImagem();
                string caminhoArquivo = Path.GetDirectoryName(caminhoNomeArquivoSelecionado);
                string nomeArquivo = Path.GetFileName(caminhoNomeArquivoSelecionado);
                //inclui a imagem na tabela
                oImagem.ExecutarInclusaoImagem(nomeArquivo,caminhoArquivo);
                MessageBox.Show("Imagem Incluída na tabela com sucesso !", "Imagem", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (SqlException ex)
            {
               MessageBox.Show(ex.ToString());
            }
            finally
            {
                oImagem.FecharConexao();
            }
        }
        private void abrirToolStripMenuItem_Click(object sender, EventArgs e)
        {
            caminhoNomeArquivoSelecionado = null;
            ofd.Filter = "GIF Files (*.gif)|*.gif|JPG Files (*.jpg)|*.jpg";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
               caminhoNomeArquivoSelecionado = ofd.FileName;
                if (caminhoNomeArquivoSelecionado.ToLower().EndsWith(".gif") || caminhoNomeArquivoSelecionado.ToLower().EndsWith(".jpg"))
                {
                    picImagem.Image = new Bitmap(caminhoNomeArquivoSelecionado);
                    btnInserir.Enabled = true;
                }
                else
                {
                    btnInserir.Enabled = false;
                }
            }
        }
        private void sairToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }
}

Destaques do código do formulário:

Após selecionar um arquivo verificamos se a sua extensão é .gif ou .jpg usando os métodos EndsWith da classe String:

    if (caminhoNomeArquivoSelecionado.ToLower().EndsWith(".gif") || caminhoNomeArquivoSelecionado.ToLower().EndsWith(".jpg"))

Obs: O método ToLower() converte o nome para caixa baixa

Para obtermos o caminho e o nome do arquivo selecionado através do OpenFileDialog estamos usando a classe Path e os métodos GetDirectoryName e GetFileName;

     string caminhoArquivo = Path.GetDirectoryName(caminhoNomeArquivoSelecionado);
     string nomeArquivo = Path.GetFileName(caminhoNomeArquivoSelecionado);


Executando o projeto teremos:

Ao clicar na opção Abrir do menu a caixa de diálogo Abrir Arquivo será exibida onde podemos selecionar uma imagem:

A imagem selecionada é exibida no controle PictureBox do formulário:

Ao clicar no botão de comando a imagem será salva no banco de dados Imagem (que será criado se não existir):

Vamos conferir no SQL Server a tabela Imagem e seu conteúdo:

Vemos que a tabela Imagem foi criada no banco de dados JcmSoft.mdf  e que o nome e o conteúdo da imagem foram salvos na tabela.

Na segunda parte deste artigo eu vou mostrar como recuperar as informações da tabela obtendo o nome e exibindo a imagem no formulário.

Pegue projeto completo aqui: InserindoImagens.zip

Veremos como exibir as imagens armazenadas no artigo: C# - Exibindo imagens armazenadas no SQL Server

"Meus filhinhos, estas coisas vos escrevo, para que não pequeis; mas, se alguém pecar, temos um Advogado para com o Pai, Jesus Cristo, o justo. " 1 João 2:1

Referências:


José Carlos Macoratti