Gerando Thumbnails em .NET (Upload em ASP.NET com jqModal e Efeitos Ajax Pt. 4)
Leonel Fraga de Oliveira 17/01/2009 00:00

Lembra do tutorial de Upload de fotos com AJAX, aquele onde fazemos o upload e exibimos as fotos em um gridview. Para você não se sentir perdido em alguns pontos deste artigo, sugiro a leitura dos artigos anteriores sobre esse assunto ;-)

Bem no final do artigo foi comentado de que poderíamos gerar um thumbnail a partir da imagem que vem do Banco de Dados. Vamos fazer isso agora?

O código completo pode ser obtido no link no final do artigo :-)

No artigo original, definimos hard-coded o tamanho das fotos que irão aparecer no gridview. Veja a declaração abaixo:

	<img runat="server" id="foto" src='< %#Bind("FotoID","visImagem.aspx?fid={0}")%>' alt="foto" width="50" height="50" />

Esta linha foi retirada do gridview da página Default.aspx do projeto original. E qual a desvantagem desta abordagem, já que o tamanho da foto vai ser reduzido?

Sim, mas de que tamanho estamos falando? Estamos falando das dimensões da foto, e não do seu tamanho em bytes!

A redução das dimensões será feita pelo navegador após o download das fotos. Uma foto de grandes dimensões, maior será seu tamanho em bytes, mais tempo leva para download, mais franquia de download gasta-se do provedor (caso o seu provedor tenha limite de download...).

Então, o que vamos fazer aqui, é recuperar a foto do BD, reduzir as suas dimensões, gerando um novo arquivo com dimensões reduzidas na memória do servidor, e enviando este novo arquivo para o usuário.

Para isso, acrescentei na classe TCadastroClsConn um método chamado GetThumbnail, que retorna um array de bytes com o novo arquivo e recebe como parâmetros as dimensões do novo arquivo.

Veja o código deste método:

public byte[] GetThumbnail(int TamX, int TamY)
{
   //Obtendo a foto original do BD
   MemoryStream sImgOriginal = new MemoryStream();
   BinaryWriter bw = new BinaryWriter(sImgOriginal);
   bw.Write(this.Foto);
   bw.Flush();

   //Cria a miniatura
  Image Original = Image.FromStream(sImgOriginal, true);
  Image Thumbnail = Original.GetThumbnailImage(TamX, TamY, new Image.GetThumbnailImageAbort(ThumbCallback), IntPtr.Zero);

  //Saída para Array de Bytes
  MemoryStream sOutput = new MemoryStream();
  Thumbnail.Save(sOutput, ImageFormat.Jpeg);

  byte[] bOutput = new byte[sOutput.Length];
  sOutput.Position = 0;
  sOutput.Read(bOutput, 0, (int)sOutput.Length);

  sImgOriginal.Dispose();
  Original.Dispose();
  Thumbnail.Dispose();
  return bOutput;
}

protected bool ThumbCallback()
{
  return false;
}

Antes, acrescentamos a referência do assembly System.Drawing ao projeto da biblioteca de classes, e além disso acrescentamos os namespaces System.Drawing e System.Drawing.Imaging na cláusula using.

Carregamos a propriedade Foto, que irá recuperar a foto do banco de dados, em um MemoryStream chamado sImgOriginal. Fazemos isso através de um BinaryWriter, que irá efetivamente escrever o conteúdo da propriedade Foto no stream.

Após isso, criamos um objeto do tipo Image, chamado Original, que inicializamos com o conteúdo da imagem no stream sImgOriginal, feito através do método estático FromStream da classe Image, que recebe como parâmetros o stream onde a imagem se encontra e um booleano que indica se será utilizado o gerenciamento de cores que está no arquivo original.

Criamos também outro objeto do tipo Image, o qual chamamos Thumbnail, que é inicializado com a saída do método GetThumbnailImage do objeto Image, que no nosso caso é a variável Original.

O método GetThumbnailImage gera uma miniatura da imagem contida em um objeto Image, e pede como parâmetros a largura, altura (inteiros), um delegate (olha ele aí) que aponta para um método de callback que representa o abort da função de geração de imagens, e a constante IntPtr.Zero.

Eu disse acima que a função GetThumbnailImage pede como um dos parâmetros um delegate, e vimos no artigo sobre Eventos que um delegate é um ponteiro para um método, não é?

Dentro da classe Image, temos o delegate Image.GetThumbnailImageAbort, que tem como assinatura o retorno de um booleano sem parâmetros. No seu construtor, informamos uma função que retorna um booleano.

No nosso caso, já que precisamos somente da referência da função, criamos a função TumbCallback, retornando false e não exigindo nenhum parâmetro. Apenas isso é o suficiente.

Pronto, geramos o thumbnail da imagem que veio do BD, e agora precisamos transformar esse novo bitmap em um array de bytes, para que possamos aproveitar todo o código restante, com pouquíssimas alterações.

Primeiramente criamos um MemoryStream para salvar o objeto Image em um Stream. Isto é feito pelo método Save() do objeto Image, que pede como parâmetros em uma das sobrecargas o stream de saída e o formato do arquivo, que é representado pela classe ImageFormat. Iremos no nosso caso utilizar o JPEG.

Com isso, criamos o nosso array de bytes que será o retorno do método, a variável bOutput. Feito isso, escrevemos o conteúdo do MemoryStream sOutput no nosso array de bytes, utilizando o método Read(), que em uma das sobrecargas recebe o array de bytes onde os dados serão escritos, a posição inicial e a quantidade de bytes a ser escrita.

Como o método Read() pede a quantidade de bytes como um inteiro, e a propriedade Length do stream nos retorna um long, precisamos fazer um cast.

Feito isso, liberamos os objetos da memória e retornamos o array de bytes com o thumbnail gerado.

Agora, na classe TCadastroClsConn temos a propriedade Foto que retorna o arquivo original e o método GetThumbnail() que retorna com dimensões personalizadas. Veja agora como ficou a linha do gridiview da página Default.aspx:

<img runat="server" id="foto" src='< %#Bind("FotoID","visImagem.aspx?fid={0}&thumb=1")%>' alt="foto" />

Na querystring da página visImagem.aspx (a que carrega a foto, lembra?), acrescentamos o parâmetro thumb. Se existir esse parâmetro, ele chama a função GetThumbnail ao invés da propriedade Foto. Veja o código da página visImagem.aspx neste ponto:

BinaryWriter bw = new BinaryWriter(Response.OutputStream);
if (Request.Params["thumb"] == null)
{
    bw.Write(cad.Foto);
}
else
{
    bw.Write(cad.GetThumbnail(120, 120));
}
bw.Flush();
bw.Close();

Somente foi acrescentado o if, mais nada!

Simples, não foi?

Exemplo Upload com Ajax + Thumbnail (229 KB)

Um abraço a todos :-)

Leonel Fraga de Oliveira Leonel Fraga de Oliveira é formado em Processamento de Dados na Faculdade de Tecnologia de São Paulo (FATEC-SP - 2002) e anteriormente em Técnico em Eletrônica, pela ETE Professor Aprígio Gonzaga (lá em 1999).
Atualmente trabalha como Analista de Sistemas na Prefeitura Municipal de São Caetano do Sul - SP
Tem como hobbies DJing (também trabalha como DJ freelancer) e ciclismo, além da manutenção dos sites NeoMatrix Light e NeoMatrix Tech.
Gosta de música eletrônica, tecnologia, cinema (super fã de Jornada nas Estrelas), gastronomia e outras coisas mais.


Compartilhe nas redes sociais

   

Deixe seu comentário

comments powered by Disqus