Desenvolvimento - ASP. NET

Armazenando imagens do SQL Server com ASP.NET

Aprenda a construir uma aplicação ASP.NET para armazenar imagens no SQL Server utilizando o Visual C#.NET.

por Carlos de Mattos



Introdução

Uma necessidade comum nas aplicações Web é o armazenamento de imagens. Existem várias formas para realizarmos esta tarefa. O SQL Server oferece um tipo de dado ideal para esta necessidade - o tipo Image - com ele você pode armazenar imagens nos mais diversos formatos (GIF, BMP, JPG, etc). Neste artigo, você aprenderá todos os passos necessários para implementar uma aplicação ASP.NET utilizando o Visual Basic.NET e o Visual C#.NET que permitirá ao usuário gravar, ler e excluir imagens no SQL Server.

Estrutura do banco de dados

Crie um novo banco de dados no SQL Server 2000 ou utilize seu banco de dados de testes para implementar a tabela tabImagens. Esta tabela armazenará além da própria imagem, uma descrição, o tamanho e o tipo de arquivo de imagem. A Listagem 1 apresenta o script necessário para criar a tabela.

if exists (select * from sysobjects where name like "%tabImagens%")
	drop table tabImagens
go

create table tabImagens
(
	ImagemID	int IDENTITY (1,1) not null,
	Descricao	nvarchar(200),
	Imagem	image,
	Tamanho	int,
	Tipo		nvarchar(200)
)
go

create unique index tabImagens_ImagemID on tabImagens
(
	ImagemID
)
go

alter table tabImagens
	add primary key (ImagemID)
go

Listagem 1: Script da tabela tabImagens

Nota: O arquivo contendo o script completo para implementação desta tabela e das stored procedures necessárias para a conclusão do exemplo apresentado neste artigo está disponível para download no site da Fórum juntamente com todos os arquivos que compõem a solução.

Stored procedures

Nosso aplicativo de exemplo deverá permitir ao usuário adicionar e excluir imagens em nosso banco de dados, consultar uma lista das imagens armazenadas e visualizar as imagens individualmente. Para facilitar a codificação e ganhar performance, as instruções SQL necessárias para esses processos serão transformadas em procedimentos armazenados (stored procedures). A Listagem 2 apresenta o código necessário para implementação desses procedimentos.

if exists (select * from sysobjects where name like "%spAdicionarImagem%")
	drop proc spAdicionarImagem
go
create proc spAdicionarImagem(
	@Descricao nvarchar(200),
	@Imagem image,
	@Tamanho int,
	@Tipo nvarchar(200)
)
as
insert into tabImagens(Descricao,Imagem,Tamanho,Tipo)
values(@Descricao,@Imagem,@Tamanho,@Tipo)
go
/*======================================================================*/ 
if exists (select * from sysobjects where name like "%spListarImagens%")
	drop proc spListarImagens
go
create proc spListarImagens
as
select ImagemID, Descricao, Tamanho, Tipo from tabImagens order by Descricao
go
/*======================================================================*/ 
if exists (select * from sysobjects where name like "%spExibirImagem%")
	drop proc spExibirImagem
go
create proc spExibirImagem(
	@ImagemID int
)
as
select * from tabImagens where ImagemID = @ImagemID
go
/*======================================================================*/ 
if exists (select * from sysobjects where name like "%spExcluirImagem%")
	drop proc spExcluirImagem
go
create proc spExcluirImagem(
	@ImagemID int
)
as
delete from tabImagens where ImagemID = @ImagemID
go

Listagem 2: Os procedimentos armazenados

Construindo a página para adicionar imagens no SQL

A primeira página que construiremos é aquela que permitirá ao usuário adicionar imagens no banco de dados. Utilizando o Visual Studio.NET, crie um novo projeto utilizando o template ASP.NET Web Application e modifique o nome do WebForm1 para Gravar_Imagem.aspx. Adicione uma tabela para organizar os controles que serão utilizados na página. Vamos trabalhar com um controle TextBox onde o usuário colocará a descrição da imagem, um controle HTML File Field que será utilizado para indicarmos o arquivo de imagem, um Button que invocará o procedimento para gravar a imagem e um LinkButton que exibirá a página Listar_Imagens.aspx.

Quero chamar a atenção do leitor para o controle HTML File Field. Quando inserimos esse controle no WebForm através da Caixa de Ferramentas o parâmetro RUNAT não está definido. É necessário adicionar esse parâmetro e atribuir o valor SERVER para que possamos fazer referências às propriedades e métodos do controle em nosso código. A Listagem 3 apresenta o código HTML necessário para implementar todos os controles.

<TABLE id="Table1">Listagem 3: O código HTML para a página Gravar_Imagem.aspx.

A interface da página Gravar_Imagem.aspx é mostrada na Figura 1.


Figura 1: A interface da página Gravar_Imagem.aspxAs classes System.Data

É importante lembrar o leitor que neste exemplo utilizaremos três WebForms (Gravar_Imagem.aspx, Listar_Imagens.aspx e Exibir_Imagem.aspx). Como em todos eles implementaremos rotinas de acesso à dados armazenados no SQL Server, o leitor deve adicionar à seção de declarações (no topo da janela de código) as referências as Classes System.Data e System.Data.SqlClient, como mostra A Listagem 4.

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

Listagem 4: Referência as classes de dados

Codificando o botão cmdGravarImagem

A principal função da página Gravar_Imagem.aspx está associada ao evento click do botão cmdGravarImagem. Esse procedimento de evento será responsável por obter as informações do arquivo de imagem através do controle HTML File Field e passá-las para o stored procedure que efetivará a gravação no banco de dados. A Listagem 5 apresenta o código necessário para esse procedimento.

private void cmdGravarImagem_Click(object sender, System.EventArgs e)
{
// esta string armazenará o tipo de imagem (JPEG, BMP, GIF, etc)
// o controle html <input type=file> fornecerá esta informação
string strTipo = fileImagemParaGravar.PostedFile.ContentType;

// o tamanho do arquivo de imagem também é fornecido pelo mesmo controle
// e armazenado na variável intTamanho
int intTamanho = System.Convert.ToInt32(fileImagemParaGravar.PostedFile.InputStream.Length);

// a variável byteImagem é um array com tamanho estabelecido pela propriedade
// length que foi armazenada na variável intTamanho (tamanho do arquivo em bytes)
byte[] byteImagem = new byte[intTamanho];

// o método Read() do controle File se encarregará de ler o arquivo de imagem 
// e armazenar o conteúdo na variável byteImagem. A sintaxe deste método é: 
// Read(<variável>, início, fim)
fileImagemParaGravar.PostedFile.InputStream.Read(byteImagem,0,intTamanho);

// esta variável armazenará a string para conexão com o SQL Server
// para facilitar nosso trabalho, a string de conexão foi armazenada 
// numa variável de aplicação. Desta forma, podemos invocá-la sempre que
// necessário e em caso de alteração das informações de conexão, precisaremos
// atualizar apenas o arquivo Global.asax
string cnString = "Data Source=localhost;UID=sa;PWD=;Initial Catalog=SQLImage";
SqlConnection Connection = new SqlConnection(cnString);

// criamos o objeto Command e definimos suas propriedades, ele será utilizado 
// para invocar a StoredProcedure que gravará a imagem no SQL Server 
SqlCommand Command = new SqlCommand();
Command.Connection = Connection;
Command.CommandType = CommandType.StoredProcedure;
Command.CommandText = "spAdicionarImagem";

// adicionamos o parâmetro @Descricao à coleção de parâmetros do objeto Command
SqlParameter prmDescricao = new SqlParameter("@Descricao", SqlDbType.NVarChar);
prmDescricao.Value = txtDescricao.Text;
Command.Parameters.Add(prmDescricao);

// adicionamos o parâmetro @Imagem à coleção de parâmetros do objeto Command
SqlParameter prmImagem = new SqlParameter("@Imagem", SqlDbType.Image);
prmImagem.Value = byteImagem;
Command.Parameters.Add(prmImagem);

// adicionamos o parâmetro @Tamanho à coleção de parâmetros do objeto Command
SqlParameter prmImagemTamanho = new SqlParameter("@Tamanho", SqlDbType.Int);
prmImagemTamanho.Value = intTamanho;
Command.Parameters.Add(prmImagemTamanho);

// adicionamos o parâmetro @Tipo à coleção de parâmetros do objeto Command
SqlParameter prmImagemTipo = new SqlParameter("@Tipo", SqlDbType.NVarChar);
prmImagemTipo.Value = strTipo;
Command.Parameters.Add(prmImagemTipo);

// abrimos a conexão, executamos o Command e fechamos a conexão
Connection.Open();
Command.ExecuteNonQuery();
Connection.Close();

// limpa a caixa de texto txtDescricao
txtDescricao.Text = "";
}

Listagem 5: Código do botão cmdGravarImagem

Entendendo como funciona o Upload da imagem

A chave para o funcionamento deste procedimento está nas propriedades e métodos do controle HTML File Field. Através dele podemos obter o path e nome do arquivo; o tamanho e o tipo de imagem; e, invocando o método Read() associado às propriedades PostedFile e InputStream, podemos alimentar nosso array de bytes que representa a imagem. Esse array é passado para o parâmetro @Imagem do nosso procedimento armazenado que efetivará a gravação da imagem no banco de dados.

O LinkButton

A única função do LinkButton inserido na página Gravar_Imagem.aspx é invocar outro WebForm: a página Listar_Imagens.aspx, que apresentará a lista de imagens gravadas no banco de dados. Para configurar esse controle utilize a Tabela 1 como referência.


Tabela 1: Propriedades do controle LinkButton

A página Listar_Imagens.aspx

Adicione um novo WebForm ao projeto e dê o nome Listar_Imagens.aspx. A função desta página é exibir uma lista com todas as imagens gravadas no SQL Server, informando o Código, Descrição, Tamanho e Tipo de imagem. Além disso a página deverá prover um link para visualizar a imagem e outro para permitir ao usuário excluir imagens do banco de dados. Para implementar essas funcionalidades, utilizaremos um controle DataGrid. A Listagem 6 mostra o código necessário para a configuração adequada do controle DataGrid.

<asp:datagrid 
id="dgImagens" 
runat="server" 
AutoGenerateColumns="False" 
OnDeleteCommand="ExcluirImagem">
<Columns>
<asp:BoundColumn DataField="ImagemID" ReadOnly="True" HeaderText="Código">
<HeaderStyle Font-Bold="True" HorizontalAlign="Center"></HeaderStyle>
<ItemStyle HorizontalAlign="Center"></ItemStyle>
</asp:BoundColumn>

<asp:BoundColumn DataField="Descricao" ReadOnly="True" HeaderText="Descrição">
<HeaderStyle Font-Bold="True" HorizontalAlign="Center"></HeaderStyle>
</asp:BoundColumn>

<asp:BoundColumn DataField="Tamanho" ReadOnly="True" HeaderText="Tamanho (Bytes)">
<HeaderStyle Font-Bold="True" HorizontalAlign="Center"></HeaderStyle>
<ItemStyle HorizontalAlign="Center"></ItemStyle>
</asp:BoundColumn>

<asp:BoundColumn DataField="Tipo" ReadOnly="True" HeaderText="Tipo">
<HeaderStyle Font-Bold="True" HorizontalAlign="Center"></HeaderStyle>
</asp:BoundColumn>

<asp:HyperLinkColumn Text="<IMG alt="Exibir Imagem" border="0" src="../../images/magnify.gif">" 
Target="_blank" DataNavigateUrlField="ImagemID" 
DataNavigateUrlFormatString="exibir_imagem.aspx?ImagemID={0}" HeaderText="Exibir">
<HeaderStyle Font-Bold="True" HorizontalAlign="Center"></HeaderStyle>
<ItemStyle HorizontalAlign="Center"></ItemStyle>
</asp:HyperLinkColumn>

<asp:ButtonColumn Text="<IMG alt="Excluir Imagem" border="0" src="../../images/error.gif">" 
HeaderText="Excluir" CommandName="Delete">
<HeaderStyle Font-Bold="True" HorizontalAlign="Center"></HeaderStyle>
<ItemStyle HorizontalAlign="Center"></ItemStyle>
</asp:ButtonColumn>
</Columns>

<PagerStyle Mode="NumericPages"></PagerStyle>
</asp:datagrid>

Listagem 6: O DataGrid para exibir a lista de imagensAs Colunas do DataGrid

O controle DataGrid é muito versátil e uma poderosa ferramenta que pode ser utilizada em diversas situações. Neste exemplo, trabalhamos com três tipos de colunas: BoundColumn, HyperLinkColumn e ButtonColumn. As colunas do tipo BoundColumn serão utilizadas para exibir as informações extraídas do banco de dados (Codigo, Descrição, Tamanho e Tipo). A coluna do tipo HyperLinkColumn será utilizada para exibir um link para visualização da imagem. A coluna do tipo ButtonColumn será utilizada para exibir um botão que invocará o procedimento para exclusão de uma imagem no banco de dados. Como o leitor pode verificar nA Listagem 5, ao contrário de trabalharmos com texto nas colunas do tipo HyperLinkColumn e ButtonColumn, utilizamos imagens. Esta prática é comum e bastante fácil de aplicar, basta substituir o texto que seria exibido no botão ou no hyperlink por um tag HTML <IMG>. Observe o resultado produzido na Figura 2.


Figura 2: A página Listar_Imagens.aspx

Codificando a página Listar_Imagens.aspx

Com um duplo-clique no corpo do WebForm você terá acesso à janela de código que exibirá o procedimento do evento Load da página. Modifique esse procedimento para invocar a rotina que alimentará o DataGrid. A Listagem 7 apresenta o código associado ao procedimento do evento Load.

private void Page_Load(object sender, System.EventArgs e)
{
	ListarImagens();
}

Listagem 7: O procedimento do evento Load

A rotina ListarImagens

Esse procedimento estabelece uma conexão com o banco de dados, dispara um procedimento armazenado através de um objeto SqlCommand e utiliza-o para alimentar um objeto SqlDataReader que servirá de fonte de dados para nosso controle DataGrid. Como mostra A Listagem 8.

private void ListarImagens()
{
// esta variável armazenará a string para conexão com o SQL Server
	// para facilitar nosso trabalho, a string de conexão foi armazenada 
	// numa variável de aplicação. Desta forma, podemos invocá-la sempre que
	// necessário e em caso de alteração das informações de conexão, precisaremos
	// atualizar apenas o arquivo Global.asax
	string cnString = "Data Source=localhost;UID=sa;PWD=;Initial Catalog=SQLImage";
	SqlConnection Connection = new SqlConnection(cnString);
	Connection.Open();
			
	// criamos o objeto Command e definimos suas propriedades, ele será utilizado 
	// para invocar a StoredProcedure que gravará a imagem no SQL Server 
	SqlCommand Command = new SqlCommand();
	Command.Connection = Connection;
	Command.CommandType = CommandType.StoredProcedure;
	Command.CommandText = "spListarImagens";

	// cria o objeto SqlDataReader e carrega-o com os dados obtidos
	SqlDataReader DataReader;
	DataReader = Command.ExecuteReader();

	// atribui o objeto SqlDataReader à origem de dados do DataGrid
	dgImagens.DataSource = DataReader;
	dgImagens.DataBind();

	Connection.Close();
}

Listagem 8: O procedimento ListarImagens()

O procedimento ExcluirImagem

Esse procedimento estabelece uma conexão com o banco de dados, invoca o procedimento armazenado para exclusão da imagem e, após a exclusão, atualiza o DataGrid. O leitor deve observar que esse procedimento é disparado sempre que o usuário clica na figura da coluna ButtonColumn. Isso ocorre porque essa coluna possui uma propriedade chamada CommandName cujo valor atribuido é igual a "Delete" e o DataGrid, por sua vez, possui um Gerenciador de Evento chamado "OnDeleteCommand" cujo valor atribuído é o nome do procedimento "ExcluirImagem" (Ver Listagem 6). Assim, quando a imagem é clicada, o DataGrid recebe a notificação que o evento Delete foi disparado e invoca o procedimento associado a ele. A Listagem 9 apresenta o código do procedimento ExcluirImagem.

protected void ExcluirImagem(Object sender, DataGridCommandEventArgs e)
{
	// estabelecemos a conexão
	string cnString = "Data Source=localhost;UID=sa;PWD=;Initial Catalog=SQLImage";
	SqlConnection Connection = new SqlConnection(cnString);
	Connection.Open();
	
	// criamos o objeto command
	SqlCommand Command = new SqlCommand();
	Command.Connection = Connection;
	Command.CommandType = CommandType.StoredProcedure;
	Command.CommandText = "spExcluirImagem";

	// adicionamos o parâmetro @Descricao à coleção de parâmetros do objeto Command
	SqlParameter prmImagemID = new SqlParameter("@ImagemID", SqlDbType.Int);
	prmImagemID.Value = e.Item.Cells[0].Text;
	Command.Parameters.Add(prmImagemID);

	// disparamos o commando para excluir a imagem
	Command.ExecuteNonQuery();
	Connection.Close();

	// invocamos a rotina ListarImagens para atualizar o DataGrid
	ListarImagens();
}

Listagem 9: O procedimento ExcluirImagem()

Visualizando as imagens

Para finalizar nosso exemplo, vamos implementar a página Exibir_Imagem.aspx. A única função desta página é exibir a imagem selecionada na lista da página Listar_Imagens.aspx. O processo utilizado é semelhante ao processo de upload da imagem. Desta vez, utilizaremos o objeto Response para indicar qual é o tipo de informação que enviaremos para o browser do internauta, isto será feito através da propriedade ContentType do objeto Response. E, através do método Write() associado a propriedade OutputStream faremos uma leitura binária do conteúdo armazenado no banco de dados que será enviado diretamente para o browser. Sabendo a natureza do conteúdo (informada através da propriedade ContentType) o browser exibe a imagem com sucesso. A Figura 3 mostra uma imagem sendo visualizada através deste WebForm.


Figura 3: Visualizando imagens

Codificando a página Exibir_Imagem.aspx

No evento Load desta página adicione uma chamada para o procedimento ExibirImagem(), como mostra A Listagem 10.

private void Page_Load(object sender, System.EventArgs e)
{
	ExibirImagem();
}

Listagem 10: O evento Load

O procedimento ExibirImagem

Esse procedimento é responsável por estabelecer a conexão, obter o registro correspondente à imagem selecionada e enviar o conteúdo binário da coluna Imagem para o browser do cliente. A Listagem 11 apresenta o código deste procedimento.

private void ExibirImagem()
{
// estabelece a conexão
string cnString = "Data Source=localhost;UID=sa;PWD=;Initial Catalog=SQLImage";
SqlConnection cn = new SqlConnection(cnString);
cn.Open();
			
// cria o objeto SqlCommand
SqlCommand cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "spExibirImagem";

// adiciona o parâmetro @ImagemID obtido através da variável de URL
SqlParameter prmImagemID = new SqlParameter("@ImagemID", SqlDbType.Int);	
prmImagemID.Value = Request.QueryString["ImagemID"];
cmd.Parameters.Add(prmImagemID);

// cria o objeto SqlDataReader e alimenta-o com a respectiva imagem
SqlDataReader dr;
dr = cmd.ExecuteReader();
dr.Read();

// utiliza o objeto Response para enviar o conteúdo para o browser
Response.ContentType = dr["Tipo"].ToString();
Response.OutputStream.Write((byte[])dr["Imagem"], 0, System.Convert.ToInt32(dr["Tamanho"]));
			
cn.Close();
}

Listagem 11: O procedimento ExibirImagem

Conclusão

Você acompanhou neste artigo os passos necessários para implementar uma aplicação que permitirá o armazenamento e gerenciamento de imagens no SQL Server.

Para se Aprofundar

Consulte também os links abaixo para obter informações complementares:

ASP.NET Developer Center
http://www.msdn.microsoft.com/asp.net/

Visual C#.NET Developer Center
http://www.msdn.microsoft.com/vcsharp/

Carlos de Mattos

Carlos de Mattos - É profissional certificado pela Microsoft desde 1998. Atua como desenvolvedor de soluções customizadas para o mercado corporativo há mais de 10 anos. Foi premiado pela Microsoft como MVP em 2003 e 2004. Tem diversos artigos publicados nas revistas FórumAccess e MSDN Magazine, nos Websites MSDN Online Brasil, Portal DevMedia/MSDN e Linha de Código. Carlos também atuou durante 5 anos junto à Comunidade Acadêmica na região de Sorocaba/SP ministrando palestras e treinamentos junto às Universidades e Colégios Técnicos. Atualmente está em Sorocaba/SP, na WD5 Solutions dedicando-se à implementação de soluções baseadas em tecnologia Microsoft.NET.