Imagens no banco de dados – Resumo

Nestes três anos, trabalhei várias formas de utilizar imagens armazenadas em banco de dados com C# e SQL Server, eu espero que tenham gostado:

Imagens no banco de dados 1 – ASP.NET Web Forms

Imagens no banco de dados 2 – ASP.NET MVC

Imagens no banco de dados 3 – Windows Forms

Imagens no banco de dados 4.1 – Relatórios em RDLC

Imagens no banco de dados 4.2 – Relatórios em RDLC

Imagens no banco de dados 5 – Importando arquivos por T-SQL

Imagens no banco de dados 5 – Importando arquivos por T-SQL

Para finalizar este série de artigos sobre técnicas de como trabalhar com imagens/arquivos no SQL Server, vamos utilizar o próprio T-SQL para realizar a importação de arquivos para dentro do banco de dados.

Pareceu difícil? Não se preocupem, é bem tranquilo.

O primeiro comando deste artigo utiliza a função OPENROWSET, especificando o caminho do arquivo, para recuperar os bytes deste arquivo em formato VARBINARY:

SELECT
	Imagem.BulkColumn AS [Bytes]
FROM
	OPENROWSET(BULK N'C:\cake.jpg', SINGLE_BLOB) AS Imagem

Para um INSERT na tabela que temos trabalhado nos artigos anteriores, basta utilizar uma instrução de INSERT..SELECT:

INSERT INTO [DemoApp].[dbo].[Arquivos]
	([Nome]
	,[Bytes]
	,[Extensao]
	,[Tipo])
SELECT
	'cake.jpg' AS [Nome],
	Imagem.BulkColumn AS [Bytes],
	'.jpg' AS [Extensao],
	'image/jpeg' AS [Tipo]
FROM
	OPENROWSET(BULK N'C:\cake.jpg', SINGLE_BLOB) AS Imagem

No caso de operações que exigiam a importação de vários arquivos de uma determinada pasta, será necessário utilizar da Stored Procedure xp_cmdshell, que necessita executar as operações a seguir para ser habilitada:

EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'xp_cmdshell', 1
GO
RECONFIGURE
GO

(Lembrando que por questões de segurança, ao termino da utilização da stored procedure xp_cmdshell, desabilitem-na)

E por meio desta Stored Procedured, poderemos relacionar todos os arquivos de uma determinada pasta do servidor de banco de dados:

DECLARE @T TABLE ( CMD NVARCHAR(255))

INSERT INTO @T
EXEC xp_cmdshell 'dir E:\Backup\Pictures\2010\Sukiaki\*.jpg /p';

SELECT SUBSTRING(CMD, 40, 215)
FROM @T
WHERE CMD LIKE '%.jpg'
GO

Por fim, unimos a ideia do INSERT..SELECT do OPENROWSET e a execução da Stored Procedure xp_cmdshell:

DECLARE @T TABLE ( CMD NVARCHAR(255))

DECLARE @PATH NVARCHAR(255)
SET @PATH = 'E:\Backup\Pictures\2010\Sukiaki\'

DECLARE @CMD NVARCHAR(255)
SET @CMD = 'dir ' + @PATH + '*.jpg /p'

INSERT INTO @T
EXEC xp_cmdshell @CMD;

DECLARE Arquivos CURSOR FOR
SELECT
	SUBSTRING(CMD, 40, 215) AS Arquivo
FROM @T
WHERE CMD LIKE '%.jpg';

OPEN Arquivos;

DECLARE @Arquivo NVARCHAR(255)
DECLARE @ArquivoPath NVARCHAR(255)

FETCH NEXT FROM Arquivos INTO @Arquivo;

WHILE @@FETCH_STATUS = 0
BEGIN
	SET @ArquivoPath = @PATH + @Arquivo;

	EXEC (
		'INSERT INTO [DemoApp].[dbo].[Arquivos]
			([Nome]
			,[Bytes]
			,[Extensao]
			,[Tipo])
		SELECT
			''' + @Arquivo + ''' AS [Nome],
			Imagem.BulkColumn AS [Bytes],
			''.jpg'' AS [Extensao],
			''image/jpeg'' AS [Tipo]
		FROM
			OPENROWSET(BULK ''' + @ArquivoPath + ''', SINGLE_BLOB)
				AS Imagem'
	)

	FETCH NEXT FROM Arquivos INTO @Arquivo;
END

CLOSE Arquivos;

DEALLOCATE Arquivos;

E teremos importado todos os arquivos:

Imagens no banco de dados 4.2 – Relatórios em RDLC

No caso de aplicações Windows Forms, também utilizaremos o ReportViewer:

E clicando sobre a seta indicada:

Definiremos o relatório a ser utilizado, e automaticamente será gerada estrutura para ligar o ReportViewer à fonte de dados:

Por fim, a aplicação em execução e a visualização do relatório:

Imagens no banco de dados 4.1 – Relatórios em RDLC

Uma funcionalidade muito útil do Visual Studio é a criação de relatórios em RDLC (Report Definition Language Client-side).

Estes relatórios em RDLC podem ser utilizando tanto em aplicações Web ou Windows Forms.

O primeiro passo para criação de um relatório em RDLC é adicionar um item Report ao projeto:

Neste relatório, vamos adicionar uma imagem estática, que nos servirá de “logo”:

Neste caso, importaremos uma imagem do computador, com o botão “Import”:

Agora temos nosso relatório com uma logo simples:

Para recuperar informações do banco de dados, precisaremos definir a nossa fonte de dados criando um Dataset:

Configuramos o banco de dados a ser utilizado por este Dataset:

E as tabelas a serem utilizadas:

Por fim, temos nosso Dataset configurado:

Adicionamos uma tabela ao nosso relatório:

Tabela adicionada e ajustada no layout do relatório:

Definimos o Dataset utilizado pela tabela:

E adicionamos uma imagem na área “Data” da tabela:

Para esta imagem que será “dinâmica”, definimos que ela virá da fonte “Database”, utilizando a campo “Bytes”, que no caso é a coluna onde estão armazenados os bytes das imagens em nossa tabela:

Por fim, adicionamos o campo “Nome” da imagem à tabela do relatório:

E ajustamos mais um pouco o layout:

No caso de aplicações Web, adicionaremos uma página simples ao projeto:

E nesta página adicionamos um ReportViewer:

Selecionando o ReportViewer, clicamos sobre a flecha indicada:

Definimos qual o relatório a ser utilizado:

Neste passo, será automaticamente criado um ObjectDataSource e já ligado ao nosso Dataset.

Por fim, adicionamos um ScriptManager a esta página:

Então teremos a página configurada com o ScriptManager, e o ReportViewer já ligado a um componente de fonte de dados (ObjectDataSource):

Agora, executar o projeto e ver o relatório com imagens do banco de dados:

Imagens no banco de dados 3 – Windows Forms

Não é somente em aplicações Web que se podem ter arquivos salvos em banco de dados, também é possível desenvolver aplicações com Windows Forms para salvar e recuperar imagens ou arquivos do banco de dados.

Para um breve exemplo, vamos criar uma aplicação Windows Forms no Visual Studio, com um formulário com um botão ‘Escolher’ para o “upload” da imagem/arquivo e um botão ‘Mostrar’ para exibir a imagem. Para a caixa de seleção do arquivo, precisaremos de um Open File Dialog e para exibição, um Picture Box:

Desta vez, estaremos utilizando Entity Framework para salvar a imagem/arquivo no banco de dados (ainda com a mesma modelagem do artigo anterior).

Ajustamos os eventos do formulário:

using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Win32;

namespace DemoApp.WinForms
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        public int ArquivoId { get; set; }

        /// <summary>
        /// Clique do botão Escolher abre OpenFileDialog
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnEscolher_Click(object sender, EventArgs e)
        {
            ofdEscolher.ShowDialog();
        }

        /// <summary>
        /// Retorno do OpenFileDialog
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ofdEscolher_FileOk(object sender, CancelEventArgs e)
        {
            var file = new FileInfo(ofdEscolher.FileName);

            var obj = new Arquivos
                          {
                              Nome = file.Name,
                              Extensao = file.Extension,
                              Tipo = GetMimeType(file.Extension)
                          };

            using (var stream = ofdEscolher.OpenFile())
            using (var reader = new BinaryReader(stream))
                obj.Bytes = reader.ReadBytes((int)stream.Length);

            using (var context = new DemoAppEntities())
            {
                context.AddObject("Arquivos", obj);
                context.SaveChanges();
            }

            // Regra somente para efeito de demonstração,
            // pois restrições devem ser criadas no Open File Dialog
            if (obj.Extensao == ".jpg")
                ArquivoId = obj.ArquivoId;
        }

        /// <summary>
        /// Recuperar Content Type do registro do Windows,
        /// por compatibilidade às aplicações Web
        /// Original.: http://www.codeproject.com/KB/dotnet/ContentType.aspx
        /// </summary>
        /// <param name="ext"></param>
        /// <returns></returns>
        public string GetMimeType(string ext)
        {
            var classesRoot = Registry.ClassesRoot;

            var typeKey = classesRoot.OpenSubKey(@"MIME\Database\Content Type");

            if (typeKey == null) return string.Empty;

            return (
                       from regExt in typeKey.GetSubKeyNames()
                       let path = @"MIME\Database\Content Type\" + regExt
                       let curKey = classesRoot.OpenSubKey(path)
                       where curKey != null
                       let value = curKey.GetValue("Extension")
                       where value != null && value.ToString().ToLower() == ext
                       select regExt
                   ).FirstOrDefault() ?? string.Empty;
        }

        /// <summary>
        /// Recuperando imagem do banco de dados e exibindo em um PictureBox
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnMostrar_Click(object sender, EventArgs e)
        {
            if (ArquivoId == 0) return;

            Arquivos obj;

            using (var context = new DemoAppEntities())
                obj = context.Arquivos.FirstOrDefault(a => a.ArquivoId == ArquivoId);

            if (obj == null) return;

            var stream = new MemoryStream(obj.Bytes);

            picImagem.Image = Image.FromStream(stream);
        }
    }
}

Agora na execução, clicamos no botão “Escolher”:

Escolhemos o arquivo para importação, no caso uma imagem JPG.

Por fim, com um clique sobre o botão Mostrar, teremos a imagem no Picture Box:

Em um cenário real, criem restrições dos tipos de arquivos no processo de importação/download da imagem/arquivo. No caso de aplicações Windows Forms, estas restrições serão no Open File Dialog.

Imagens no banco de dados 2 – ASP.NET MVC

Quem aderiu ao ASP.NET MVC também pode salvar imagens ou arquivos no banco de dados? Mas é claro e para provar isso, vamos ao exemplo.

Após criar a tabela no banco de dados (conforme o artigo anterior), o primeiro passo será definir a tecnologia de acesso aos dados. No caso utilizaremos LINQ to SQL:

Para a interface, a nossa Index.cshml necessitará somente do seguinte código:

@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <div>
        <input id="file" name="file" type="file" />
    </div>
    <div>
        <button type="submit">
            Enviar</button>
    </div>
}

No caso, estou utilizando Razor, mas é fácil adequar para ASPX.

E na Controller, teremos os métodos Index para salvar o arquivo no banco de dados e o método Viewer para recuperar o arquivo do banco de dados:

using System.IO;
using System.Linq;
using System.Web.Mvc;

namespace DemoApp.AspMvc.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            return View();
        }

        //
        // POST: /Home/

        [HttpPost]
        public ActionResult Index(FormCollection form)
        {
            if (Request.Files.Count != 1) return View();

            var post = Request.Files[0];

            if (post == null) return View();

            var obj = new Models.Arquivo();

            var file = new FileInfo(post.FileName);
            obj.Nome = file.Name;
            obj.Extensao = file.Extension;
            obj.Tipo = post.ContentType;

            using (var reader = new BinaryReader(post.InputStream))
                obj.Bytes = reader.ReadBytes(post.ContentLength);

            using (var context = new Models.DemoAppDataContext())
            {
                context.Arquivos.InsertOnSubmit(obj);
                context.SubmitChanges();
            }

            return RedirectToAction("Viewer", new { id = obj.ArquivoId });
        }

        //
        // GET: /Home/Viewer/id

        public ActionResult Viewer(int id)
        {
            ActionResult result;

            using (var context = new Models.DemoAppDataContext())
            {
                var obj = context.Arquivos.FirstOrDefault(a => a.ArquivoId == id);

                result = obj == null
                    ? (ActionResult)View("Error")
                    : new FileContentResult(obj.Bytes.ToArray(), obj.Tipo);
            }

            return result;
        }
    }
}

Por fim, a página de upload:

E a página de visualização do arquivo:

E ai Paulo, já acabou esta série de artigos? Não, ainda teremos outros exemplos, aguardem!

Imagens no banco de dados 1 – ASP.NET Web Forms

Atendendo ao pedido do Luiz H. S. Pereira, vamos ver um pouco como trabalhar com aplicações e imagens/arquivos salvos em banco de dados.

Neste primeiro artigo, veremos como fazer isso com ASP.NET Web Forms.

O primeiro passo é a tabela onde serão armazenados os arquivos:

USE DemoApp
GO
CREATE TABLE Arquivos (
	ArquivoId	INT IDENTITY	PRIMARY KEY,
	Nome		VARCHAR(250)	NOT NULL,
	Bytes		VARBINARY(MAX)	NOT NULL,
	Extensao	CHAR(5)			NOT NULL,
	Tipo		VARCHAR(250)	NOT NULL
)

O segundo passo é criar uma página com o componente FileUpload (fupArquivo) e um Button (btnEnvio) para o envio:

Trataremos o evento do Button de envio, salvando os dados do arquivo no banco de dados:

using System;
using System.Data.SqlClient;
using System.IO;
using System.Web.UI;

namespace DemoApp.WebForms
{
    public partial class Default : Page
    {
        protected void btnEnviar_Click(object sender, EventArgs e)
        {
            string url;

            const string connString =
                @"Data Source=localhost\SQLEXPRESS;Initial Catalog=DemoApp;Integrated Security=True";

            using (var conn = new SqlConnection(connString))
            {
                conn.Open();

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText =
                        "INSERT INTO [Arquivos] VALUES (@Nome, @Bytes, @Extensao, @Tipo)"
                        + " SELECT SCOPE_IDENTITY()";

                    var file = new FileInfo(fupArquivo.FileName);
                    cmd.Parameters.AddWithValue("@Nome", file.Name);
                    cmd.Parameters.AddWithValue("@Bytes", fupArquivo.FileBytes);
                    cmd.Parameters.AddWithValue("@Extensao", file.Extension);
                    cmd.Parameters.AddWithValue("@Tipo", fupArquivo.PostedFile.ContentType);

                    var codigo = cmd.ExecuteScalar();

                    url = string.Format("ImageHandler.ashx?id={0}", codigo);
                }
            }

            Response.Redirect(url);
        }
    }
}

Para exibição do arquivo salvo no banco de dados, criaremos um Generic Handler:

Com Handler criado:

Configuraremos o Handler para recuperar o arquivo do banco de dados:

using System.Data.SqlClient;
using System.Web;

namespace DemoApp.WebForms
{
    /// <summary>
    /// Summary description for ImageHandler
    /// </summary>
    public class ImageHandler : IHttpHandler
    {
        public bool IsReusable { get { return false; } }

        public void ProcessRequest(HttpContext context)
        {
            var codigo = context.Request.QueryString["id"];

            if (codigo == null) return;

            const string connString =
                @"Data Source=localhost\SQLEXPRESS;Initial Catalog=DemoApp;Integrated Security=True";

            using (var conn = new SqlConnection(connString))
            {
                conn.Open();

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText =
                        "SELECT Tipo, Bytes FROM [Arquivos] WHERE ArquivoId = @ArquivoId";

                    cmd.Parameters.AddWithValue("@ArquivoId", codigo);

                    var dr = cmd.ExecuteReader();

                    if (dr.Read())
                    {
                        context.Response.ContentType = dr.GetString(0);

                        context.Response.BinaryWrite((byte[])dr[1]);
                    }
                }
            }
        }
    }
}

Por fim, importamos o arquivo:

Se tudo der certo, o arquivo será exibido: