Como prometi em um post anterior, vou detalhar aqui alguns dos conceitos e motivações por trás do gerador de thumbnails que disponibilizei no CodePlex como parte integrante de uma biblioteca para agilizar desenvolvimento ASP.NET chamada CNZK Web Library. O código que está no CodePlex não contém ainda uma documentação formal mas está bem comentado. Vou mostrar o motivo de algumas das decisões que tomei no desenvolvimento dessa classe e tentar explicar o funcionamento de alguns trechos de código.
Para começar: As Motivações
Eu coordeno um time de desenvolvimento responsável por diversos projetos dos nossos clientes. Para poder dar conta de todos os projetos de desenvolvimento ou manutenção de sites com eficiência e agilidade foi necessário desenvolver e aplicar metodologias e padrões. Durante algumas reuniões nós tentamos identificar pontos em comum entre os projetos que pudessem ser padronizados e transformados em uma biblioteca para evitar que o trabalho fosse refeitos todas as vezes. Um ponto que era muito importante para mim era que a utilização dessa biblioteca fosse o mais simples possível, para encorajar o seu uso e aperfeiçoamento.
A geração de thumbnails de imagens pode não parecer muito óbvia quando se analisa os pontos em comum entre sites, até o momento em que você começa a olhar o código das áreas de administração do site e vê a complexidade que é embutida no código para fazer upload de 2 ou 3 imagens para cada produto ou item de uma página. Além disso, tem mais outros 2 fatores que foram muito importantes na decisão de fazer o gerador de thumbnails: Gerenciamento de muitos arquivos em disco no webserver; Facilidade e agilidade de uso da área administrativa do site.
Essa biblioteca do Codeplex não contemplará apenas o gerador de thumbnails. Há diversas outras classes que já tenho prontas ou parcialmente prontas que estão apenas passando por uma limpeza de código antes de fazerem parte da biblioteca. A classe de thumbnail foi escolhida para ser a primeira por ser a que estava em estado mais maduro e por ter sido a melhor testada e mais utilizada da biblioteca até agora em nossos projetos.
Segundo passo: Decisões Técnicas e Arquitetura
A idéia inicial era fazer uma classe que tornasse possível redimensionar facilmente as imagens no momento do upload, para que o usuário não precisasse fazer upload de2 ou 3 imagens diferente apenas por causa do tamanho. Após fazer uma primeira versão eu notei que isso não agilizaria muito o processo de desenvolvimento e também não seria um componente muito reutilizável.
Então eu encontrei um capítulo em um livro que mostrava como fazer um HttpHandler para redimensionamento de imagem. O código no livro trabalhava de forma semelhante ao código que eu gerei, mas o tamanho final das imagens não era parametrizável e as miniaturas geradas eram armazenadas em disco. Essas duas características ainda não eram exatamente o que eu queria, então eu fiz a minha versão.
HttpHandlers são um recurso extremamente interessante do ASP.NET, pois nos possibilitam criar a nossa própria extensão de arquivo e tratá-la da forma que precisarmos, como se fosse um novo tipo de arquivo. O único problema em se criar sua própria extensão de arquivo é que será necessário regitrar essa extensão no IIS e informar que ela deverá ser processada pelo ASP.NET para que o código do seu handler seja executado (aliás, é isso que o ASP.NET faz com as extensões aspx, ascx e axd). A extensão axd já é utilizada pelo ASP.NET justamente para processamento de handlers (um exemplo disso é o trace.axd).
Então, o que eu fiz foi implementar uma classe extendendo IHttpHandler e registrar a minha classe como sendo o handler para qualquer chamada que for feita para arquivos com a extensão ".thumb.axd". Para o IIS, o que interessa é a extensão axd, que indica que o ASP.NET deve processar a requisição. Eu poderia ter usado apenas a extensão ".thumb", mas teria que registrá-la no IIS do servidor do site, o que não seria muito prático, principalmente levando em consideração a quantidade de ambiente que precisariam ser configurados (desenvolvimento, homologação, produção, etc...).
Como Funciona
Eu tentei tornar a utilização simples, rápida e pouco intrusiva. Uma das vantagens de utilizar a extensão .axd, é que o IIS está configurado para simplesmente repassar qualquer requisição feita à arquivos com essa extensão para o ASP.NET, sem sequer verificar se o arquivo realmente existe. Isso tornou possível que eu utilizasse a extensão que escolhi (.thumb.axd) como sufixo das imagens originais. Assim eu poderia chamar um thumb para uma imagem usando a seguinte URI: "/img/foto.jpg.thumb.axd".
Agora tudo que eu precisava era passar alguns parâmetros para que o handler soubesse qual deveria ser o novo tamanho da imagem. Como eu não queria que a url ficasse poluída, fiz com que os parâmetros de tamanho (que são obrigatórios) fossem passados como se fizessem parte do nome do arquivo, da seguinte forma: "/img/foto.jpg.200x150.thumb.axd". Pronto, agora posso redimensionar qualquer imagem nos meus sites. Outra vantagem é que os profissionais de interface podem utilizar esse controle sem nenhuma interferência dos programadores, poupando trabalho dos 2 lados.
Superficialmente, a lógica que o código segue é a seguinte:
- Verifica se a URI solicitada está no formato correto, utilizando Expressão Regular.
- Obtém a imagem que será redimensionada. Verifica se a imagem original existe.
- Se existir, abre o arquivo e obtém suas dimensões. Nesse momento é configurado o cache, vinculado ao arquivo original.
- Se não existir, verifica se está configurado um arquivo padrão para imagem não disponível e o abre.
- Se não houver arquivo padrão, cria uma imagem em memória com os dizeres "Imagem não disponível".
- Faz o cálculo das proporções da imagem, para verificar se o tamanho solicitado é proporcional ao original.
- Se o tamanho solicitado for proporcional, redimensiona a imagem para o novo tamanho.
- Se não for proporcional, calcula o redimensionamento da imagem para que caiba no novo tamanho.
- Cria uma nova imagem em memória com o tamanho solicitado.
- Redimensiona a imagem obtida (original ou de aviso) para o tamanho proporcional calculado.
- Coloca a imagem redimensionada centralizada dentro da nova imagem.
- Define qual será o tipo de output (se o original for gif, a saída será gif. se não for gif, a saída será jpg).
- Faz o output da imagem, codificada no formato correto.
Alguns pontos interessantes desse handler:
Os thumbnails gerados são armazenados em cache para evitar sobrecarga de processamento no servidor. Esse cache está vinculado ao arquivo da imagem original, ou seja, se a imagem original for alterada o cache expira.
Sempre será retornada uma imagem, existindo ou não a original. Isso poupa bastante código de verificação nas páginas e problemas de layout.
A imagem redimensionada não será distorcida. Se a imagem original não estiver nas mesmas proporções da solicitada, ela será redimensionada proporcionalmente para caber no tamanho solicitado.
O peso das páginas vai diminuir, poupando banda dos usuários do site e tornando o carregamento da página mais rápido.
Outros parâmetros
Esse controle não faz apenas redimensionamento de imagens. Conforme as necessidades foram surgindo, novas funcionalidades foram desenvolvidas. Abaixo tem uma lista do que o controle faz, além de redimensionar imagens:
- Definição da cor de fundo. É possível definir a cor de fundo utilizando o parâmetro "bg" na URI. Essa cor será visível quando a imagem for de proporções diferentes da solicitada ou quando for gerada a "imagem não diponível".
- Definição da cor de frente. Parâmetro "fg". É a cor que será utilizada no texto de "imagem não disponível".
- Definição de texto. Parâmetro "txt". É o texto que aparecerá em "Imagem não disponível".
- Definição do método do cálculo de proporção. Parâmetro "inside". Define se a imagem deve ser redimensionada para caber inteira dentro da nova proporção ou se haverá cortes nas bordas. Os cortes são feitos em apenas 2 lados, no caso de inside=false.
- Definição de máscara. Parâmetro "mask". É possível colocar uma máscara translúcida sobre a imagem. A cor e opacidade da máscara são fornecidas no formato ARGB, em um hexadecimal de 8 caracteres. Ex.: mask=88FF0000 é igual a uma máscara vermelha com 50% de opacidade.
- Definições globais no web.config. É possível definir valores padrão para todos os parâmetros acima, exceto a máscara, no web.config da aplicação.
Documentação
Estou finalizando uma documentação completa do código, com exemplos e detalhes de todos os parâmetros. Essa documentação será disponibilizada no Codeplex ainda nesta semana.
Resumo
Expliquei um pouco sobre o motivo da criação dessa biblioteca e detalhei melhor o funcionamento do gerador de thumbnails. Achei melhor não colocar nenhum código neste post pois o objetivo aqui não era ser técnico. Quem quiser mais detalhes, pode baixar o código fonte no projeto CNZK Web Library no Codeplex e dar uma olhada. Se houver qualquer dúvida ou sugestão, basta postar um comentário no Codeplex neste blog.