Upload em ASP.NET com jqModal e Efeitos Ajax Pt. 3 -> Exibindo e fazendo download da foto do BD
Olá pessoal! Chegamos ao último artigo da série que ensina como fazer um upload de arquivo quando utilizamos o UpdatePanel do ASP.NET e janelas modais, porém sem perder a navegação no estilo AJAX (como é do nosso conhecimento, o FileUpload do ASP.NET necessita de um postback para ser executado).
Na primeira parte, foi explicado como fazemos um upload de um arquivo em janelas modais e UpdatePanel, utilizando iframes para manter a navegação no estilo AJAX e salvar o resultado do upload no sistema de arquivos do servidor; na segunda parte, como salvar esse arquivo em um banco de dados utilizando a Classe de Conexão e diretamente pelo provider do banco de dados (o Firebird é usado como exemplo).
Nesta terceira e última parte do tutorial, vou detalhar como exibimos em uma página e disponibilizamos para download esta foto que recuperamos do banco de dados.
Por incrível que pareça, esta é a parte mais simples de todas. Como todas as outras partes, assistam a vídeo-aula (screencast) e vejam a explicação em texto abaixo.
E os arquivos utilizados, podem ser obtidos na nossa página de suporte no final do artigo (cliquem no link para fazer o download)!
No artigo anterior, vimos que através de um comando SELECT trazemos para o nosso objeto TCadastro***** o campo BLOB que corresponde ao nosso arquivo, e que na classe ele é apenas um array de bytes sem distinção de formato, essas coisas. Isso na biblioteca de classes.
Na página Default.aspx do nosso exemplo, especialmente na janela modal dvEditarFoto temos um label que irá receber um comando HTML com a seguinte estrutura: <img src="visImagem.aspx?fid=xxx" ... /> e que no atributo src temos uma página .aspx que recebe em sua querystring o código da foto que iremos editar.
No exemplo deste artigo, temos algumas pequenas mudanças no design da página Default.aspx: No gridiview temos um botão que faz o download da foto, e em seu evento OnClick temos o registro da função JavaScript em Consts.Javascript.openWindow, que nada mais é que a montagem de um comando window.open, com a ajuda do registro da constante Consts.Javascript.ScriptPopUp no evento Page_Load. Ele abre a mesma página visImagem.aspx, porém recebendo outro parâmetro em sua querystring (d=1), além do ID da foto.
Também foi colocado uma tag <img> no gridview, que tem no seu atributo src a mesma página visImagem.aspx recebendo em sua querystring o ID da foto. Uma coisa interessante é a forma como eu monto essa tag <img> dentro da TemplateColumn do gridview:
1: <asp:TemplateField>
2: <ItemTemplate>
3: <asp:Button runat="server" ID="btnEditar" Text="Editar" CommandArgument='<%#Bind("FotoID")%>' OnClick="btnEditar_Click" />
4: <asp:Button runat="server" ID="btnDownload" Text="Download" CommandArgument='<%#Bind("FotoID")%>' OnClick="btnDownload_Click" />
5: <asp:Button runat="server" ID="btnExcluir" Text="Excluir" CommandArgument='<%#Bind("FotoID")%>' OnClick="btnExcluir_Click" OnClientClick="return confirm('Deseja excluir?');"/>
6: <img runat="server" id="foto" src='<%#Bind("FotoID","visImagem.aspx?fid={0}")%>' alt="foto" width="50" height="50" />
7: </ItemTemplate>
8: </asp:TemplateField>
Esta tag <img> possui o atributo runat="server", pois para alimentar a propriedade (sim, agora chamo assim exatamente por causa do runat="server" :-) ) utilizo-me da diretiva #Bind (que necessita que o controle onde vamos usar essa diretiva tenha o atributo runat="server" e consequentemente um ID), que no primeiro parâmetro recebe o nome do campo (ou propriedade da classe) que queremos que seja impresso na página e no segundo parâmetro uma string de formatação, que no nosso caso é "visImagem.aspx?fid={0}", onde "{0}" será substituído pelo ID da foto (por exemplo, você poderia usar aí qualquer string de formatação do .NET, como datas, moeda, etc, utilizando para isso "{0:<string de formatação do .NET>}", ex: "{0:dd/MM/yyyy}" para formatar uma data.
Como vocês já devem ter notado e se perguntado... o lance é a página visImagem.aspx, não é? Resposta: Sim, a própria!
Em ambos os casos (tanto visualizando a foto na página Default.aspx através do modal dvEditarFoto quanto na opção para download a página visImagem.aspx É o próprio arquivo de imagem que queremos recuperar. Mas como isso é feito, já que uma página .aspx é interpretada pelo compilador asp.net como uma página HTML?
Vou mostrar aqui apenas o code-behind da página visImagem.aspx, já que o design não possui componentes (possui somente as declarações do asp.net, tags head, html e body e a tag form). Isso mesmo, é só criar a página pelo Visual Studio e ir direto no code-behind :-)
1: using System;
2: using System.Data;
3: using System.Configuration;
4: using System.Collections;
5: using System.Web;
6: using System.Web.Security;
7: using System.Web.UI;
8: using System.Web.UI.WebControls;
9: using System.Web.UI.WebControls.WebParts;
10: using System.Web.UI.HtmlControls;
11: using System.IO;
12: using AppCore;
13:
14: namespace UploadAJAX
15: {
16: public partial class visImagem : System.Web.UI.Page
17: {
18: protected void Page_Load(object sender, EventArgs e)
19: {
20: if (Request.Params["fid"] != null)
21: {
22: Response.Buffer = true;
23: Response.ContentType = "image/jpeg";
24: TCadastroClsConn cad = new TCadastroClsConn();
25: try
26: {
27: cad.FotoID = Int32.Parse(Request.Params["fid"].ToString());
28: if (cad.Foto.Length != 0)
29: {
30: string att = "inline";
31: if (Request.Params["d"] != null)
32: {
33: att = "attachment";
34: }
35:
36: Response.AddHeader("Content-Disposition", att + "; filename=foto" + cad.FotoID.ToString() + ".jpg");
37: BinaryWriter bw = new BinaryWriter(Response.OutputStream);
38: bw.Write(cad.Foto);
39: bw.Flush();
40: bw.Close();
41: }
42: }
43: finally
44: {
45: cad.Dispose();
46: }
47: Response.End();
48: }
49: }
50: }
51: }
A "transformação" de um arquivo .aspx em um arquivo qualquer (sim, este procedimento serve para recuperarmos QUALQUER tipo de arquivo) consiste em alterar o tipo de conteúdo no cabeçalho da resposta HTTP e colocarmos o arquivo desejado no stream de saída (o que vai para o navegador) da página.
No evento Page_Load, checamos se o parâmetro "fid" (que é informado na querystring) existe.
Setamos a propriedade Response.Buffer para true, isto indica que o que estiver no buffer de saída será enviado após processarmos a resposta (no nosso caso, com o método Response.End no final do evento Page_Load).
Como estamos trabalhando com imagens, e qualquer navegador hoje em dia trabalha com arquivos .jpg nativamente, colocamos no MIME type de resposta o valor "image/jpeg", que corresponde ao MIME type para arquivos .jpg. Isto é feito através da propriedade Response.ContentType.
Um parêntese aqui: E se o arquivo for um gif ou PNG? Neste caso, no banco de dados teríamos que gravar a extensão do arquivo que fizemos upload e precisaríamos de uma tabela auxiliar de MIME types, para setarmos a resposta corretamente. Mas como isso foge do escopo da série de artigos, deixo para você pensar um pouco :-)
Em seguida, instanciamos a classe TCadastroClsConn (poderia ser a sem a Classe de Conexão também), setando na propriedade FotoID o parâmetro "fid". Se o conteúdo da propriedade Foto (um array de bytes) for diferente de zero, vamos para o pulo do gato:
Criei uma variável chamada "att" do tipo string com o valor "inline". Se a querystring tiver um parâmetro chamado "d" ("d" de "Download"), não importando o valor, mudo essa variável para "attachment".
Explico: Logo na linha abaixo, inserimos no cabeçalho da resposta HTTP, através do método Response.AddHeader a chave "Content-Disposition", que indica como o navegador irá disponibilizar para o usuário o conteúdo que está sendo enviado pelo servidor web. Como valores possíveis, temos:
- inline: o conteúdo será disponibilizado através da área de visualização do navegador.
- attachment: o conteúdo será direcionado para o gerenciador de downloads do navegador.
Se o parâmetro "d" não existir na querystring, iremos enviar "inline", caso contrário, "attachment".
Como complemento do valor da chave "Content-Disposition", informo o atribiuto "filename", que será o nome do arquivo que irá aparecer no gerenciador de downloads do navegador quando baixamos esse arquivo.
Caso a opção seja por visualizar no navegador, ele irá renderizar o conteúdo que enviamos no stream de saída como um arquivo jpg (como informamos anteriormente no Response.ContentType), porém, quando vemos as propriedades desta imagem, vemos a URL que colocamos na tag <img> em Default.aspx, que é a própria página visImagem.aspx, como na imagem abaixo
Setamos como vai ser o tipo e a disposição do conteúdo de saída da página, agora só falta setar o conteúdo em si :-)
O que será enviado para o navegador do usuário está na propriedade Response.OutputStream do objeto Page (a própria página hehe). Ele é do tipo Stream.
Iremos colocar no OutputStream a foto que iremos enviar para o usuário, e ela é um array de bytes em que cada posição está cada byte do arquivo de foto. Para colocar este array de bytes no OutputStream, vamos fazer uso da classe BinaryWriter, que está no namespace System.IO, criando um objeto chamado "bw".
Ao instanciar a classe BinaryWriter no objeto bw, colocamos em seu construtor o stream de destino, que no nosso caso é o próprio Response.OutputStream. Em seguida, chamamos o método Write() do BinaryWriter que em uma das sobrecargas recebe como parâmetro um array de bytes (justo o tipo que estamos trabalhando :-) ); informamos neste parâmetro a propriedade Foto do nosso Cadastro (que por sua vez, irá buscar a foto que está em FotoID no banco de dados, através do método getRawFile, lembra?), para em seguida chamar o método Flush() e fecharmos o BinaryWriter.
Por fim, chamamos o método Response.End() que irá enfim enviar a foto para o usuário.
Este método serve para obtermos QUALQUER tipo de arquivo que gravamos em um banco de dados E não salvamos em hipótese alguma na estrutura de diretórios do servidor web. Pode ser foto, mp3, pdf, enfim, qualquer um, bastando informar o MIME type correto no Response.ContentType, e tomar cuidado ao usar os meios "inline" e "attachment" dependendo do tipo de arquivo, por exemplo, para .exe usar o meio "attachment".
Fácil, não foi?
Um abraço a todos, e até o próximo artigo (quem sabe... não vamos aprender a gerar um thumbnail através da imagem que gravamos no BD?)!
0 comentários:
Postar um comentário