+6

PHP + Imagens (Gerando thumbnails - imagens miniatura)

criado por Mauricio Wolff em 27/05/2004 12:54pm
Intro
Um belo dia você acorda, toma um café preto e uma aspirina e decide colocar as fotos da festa de ontem na internet. Aquela, que fez você ter de tomar uma aspirina e o café preto.

Você tem na sua frente um bom editor de textos, um Apache com PHP, um browser e 60 fotos para botar no ar. Melhor fazer de uma vez, antes que a vontade passe.

Possibilidades
Para ser rápido, você pensa em botar as fotos uma em cima da outra, separadas por um <br />, e era isso. Mas aí você tem de aturar os amigos reclamando que a página demora um século para abrir e tem muita rolagem.

Também pode fazer o resize direto no HTML, forçando o IMG tag para 160x120px, e as suas fotos demorariam igual para carregar e ficariam com uma aparência pior que a sua neste momento.

A última maluquice seria fazer thumbnails (miniaturas) das fotos num editor gráfico, e criar um link para a foto em tamanho natural. Genial! Mas fazer thumbnails de 60 imagens??? E quando os outros mandarem as fotos deles para serem incluídas, você vai fazer tudo de novo? Larry Wall já disse que o bom programador é meio preguiçoso. "Faça bem feito para não ter de fazer de novo"

A Solução
Aí então, você lembra de, um dia, ter lido em algum lugar que o PHP trabalha com imagens.
Bem lembrado. Neste artigo você vai aprender a criar thumbnails em realtime com o PHP e a biblioteca GD.

Para isso tenha certeza de que o seu PHP foi compilado com suporte a GD 2.0.1 (no win32 é padrão. No *nix, --with-gd[=DIR]) e que ele está sendo carregado no PHP.INI. Se você está hospedando o seu site numa empresa de host, peça gentilmente para o admin habilitar o GD2 no PHP.INI. Não compromete em nada a performance do server e dá uma possibilidade a mais para os clientes.

O script vai receber a imagem da qual o thumbnail deve ser gerado pela querystring, com o endereço completo da imagem no servidor. Uma IMG tag vai ficar mais ou menos assim:

<img src="thumb.php?dir/imagem.jpg">

Explicações dadas, vamos ao que interessa: show me the code...

Código-Fonte

<!-- START arquivo thumb.php -->

<?PHP
/** Arquivo: thumb.php
  * Autor: Mauricio Wolff .:. organiKa
  * baseado no trabalho de Michael Bailey
***/

// Constantes: variaveis que não mudam em todo o programa
// path do win2k. no unix, mude de 'd:/path' para '/home/usuario/www'
define('PATH_IMG', 'd:/www/artigos/thumb');
define('MAX_WIDTH', 160);
define('MAX_HEIGHT', 120);

# Pega onde está a imagem
$image_file = str_replace('..', '', $_SERVER['QUERY_STRING']);
$image_path = PATH_IMG . '/' . $image_file;

# Carrega a imagem
$img = null;

$extensao = strtolower(end(explode('.',$image_path)));

if ($extensao == 'jpg' || $extensao == 'jpeg') {
    $img = @imagecreatefromjpeg($image_path);
} else if ($extensao == 'png') {
    $img = @imagecreatefrompng($image_path);
    // Se a versão do GD incluir suporte a GIF, mostra...
} elseif ($extensao == 'gif') {
    $img = @imagecreatefromgif($image_path);
}

// Se a imagem foi carregada com sucesso, testa o tamanho da mesma
if ($img) {
    // Pega o tamanho da imagem e proporção de resize
    $width = imagesx($img);
    $height = imagesy($img);
    $scale = min(MAX_WIDTH/$width, MAX_HEIGHT/$height);

    // Se a imagem é maior que o permitido, encolhe ela!
    if ($scale < 1) {
        $new_width = floor($scale * $width);
        $new_height = floor($scale * $height);
        // Cria uma imagem temporária
        $tmp_img = imagecreatetruecolor($new_width, $new_height);
        // Copia e resize a imagem velha na nova
        imagecopyresized($tmp_img, $img, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
        imagedestroy($img);
        $img = $tmp_img;
    }
}

// Cria uma imagem de erro se necessário
if (!$img) {
    $img = imagecreate(MAX_WIDTH, MAX_HEIGHT);
    imagecolorallocate($img, 204, 204, 204);
    $c = imagecolorallocate($img, 153, 153, 153);
    $c1 = imagecolorallocate($img, 0, 0, 0);
    imageline($img, 0, 0, MAX_WIDTH, MAX_HEIGHT, $c);
    imageline($img, MAX_WIDTH, 0, 0, MAX_HEIGHT, $c);
    imagestring($img, 2, 12, 55, 'erro ao carregar imagem', $c1);
}

// Mostra a imagem
header('Content-type: image/jpeg');
imagejpeg($img);
?>
<!-- END arquivo thumb.php -->

Comentários
Agora que você já saciou a sede de código, prepare uma xícara de café (ou JoltCola... eu nunca tomei, não posso falar) e vamos analisar o código como faria Jack, por partes...

<?php
// Constantes: variaveis que não mudam em todo o programa
// path do win2k. no unix, mude de 'd:/path' para '/home/usuario/www'
define('PATH_IMG', 'd:/www/artigos/thumb');
define('MAX_WIDTH', 160);
define('MAX_HEIGHT', 120);
?>

Primeiro, a definição de algumas constantes. "PATH_IMG" aponta para o diretório onde estãos os arquivos de imagem no servidor. "Apenas imagens neste ou nos seus subdiretórios serão mostradas". Não é um primor de segurança, mas funciona muito bem.

MAX_WIDTH e MAX_HEIGHT definem o tamanho máximo em pixels para os thumbnails. Se a imagem já possuir um tamanho menor que estes, ela não é alterada. Proporções serão mantidas, pois se a largura (width) for reduzida por 3, a altura (height) será reduzida na mesma razão.

<?php
# Pega onde está a imagem
$image_file = str_replace('..', '', $_SERVER['QUERY_STRING']);
$image_path = PATH_IMG . '/' . $image_file;

# Carrega a imagem
$img = null;

$extensao = strtolower(end(explode('.',$image_path)));

if ($extensao == 'jpg' || $extensao == 'jpeg') {
    $img = @imagecreatefromjpeg($image_path);
} else if ($extensao == 'png') {
    $img = @imagecreatefrompng($image_path);
    // Se a versão do GD incluir suporte a GIF, mostra...
} elseif ($extensao == 'gif') {
    $img = @imagecreatefromgif($image_path);
}
?>

O local físico da imagem é passada na query string, como explicado nos comentários. Para prever o acesso de imagens fora do local especificado na constante, quaisquer ".." são suprimidos da query string, e ½ dos "h4x0rs" eliminados ($image_file = str_replace('..', '', $_SERVER['QUERY_STRING']);)

Em seguida, a extensão do arquivo é checada para se definir qual função do GD será usada para a imagem em questão. O GD pode criar imagens a partir de: "gif, jpeg, png, wbmp, xbm, xpm"... etc. Neste script você vai encontrar suporte para "gif, jpeg e png", e isso deve bastar. Caso precise acrescentar, ponha mais um elseif e resolvido!

Se a extensão não for reconhecida ou o arquivo não existir, a variável $img vai ficar vazio e isso vai gerar um erro. O @ antes das funções previne que erros sejam mostrados se a função imagecreatefrom... não conseguir criar a imagem. Essa mesma técnica pode ser usada em extract, se o argumento passado não for um array.

Se você tentar utilizar este script para gerar o thumbnail de um ".gif" e obtiver o erro:
"Warning: ImageCreateFromGif: No GIF read support in this PHP build"
Não se desespere! Isso acontece porque na versão 1.6 o GD deixou de oferecer suporte a .gif, já que a dona Unisys tem a patente do algoritmo de compressão usado nos gifs e quer $$$ por isso.

Existem várias alternativas para contornar este problema. Em distribuições Linux como o Debian você pode baixar pacotes RPM do GD patched, você mesmo pode aplicar o patch no gd.c, baixar patches na internet (tente <a href="http://www.webofsin.com/gd-1.8.3-gif.patch">http://www.webofsin.com/gd-1.8.3-gif.patch</a>) ou usar programas externos para converter os ".gifs em .pngs" (gif2png, mogrify, ImageMagick, netPBM).

<?php
// Se a imagem foi carregada com sucesso, testa o tamanho da mesma
if ($img) {
    // Pega o tamanho da imagem e proporção de resize
    $width = imagesx($img);
    $height = imagesy($img);
    $scale = min(MAX_WIDTH/$width, MAX_HEIGHT/$height);
?>

imagesx() e imagesy() retornam a largura e altura da imagem respectivamente. A proporção de redimensionamento é obtida com o "tamanho máximo permitido" dividido pelo "tamanho real" da imagem. Ele é calculado para a largura (width) e altura (height), mas somente o menor valor deles é usado. Usando a mesma razão para altura e largura, a proporção é mantida.

<?php
// Se a imagem é maior que o permitido, encolhe ela!
    if ($scale < 1) {
        $new_width = floor($scale * $width);
        $new_height = floor($scale * $height);
        // Cria uma imagem temporária
        $tmp_img = imagecreatetruecolor($new_width, $new_height);
        // Copia e resize a imagem velha na nova
        imagecopyresized($tmp_img, $img, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
        imagedestroy($img);
        $img = $tmp_img;
    }
}
?>

Se a proporção de redimensionamento ($scale) for maior ou igual a 1, a imagem não precisa ser alterada, já é menor que um thumbnail. Caso contrário, a imagem precisa ser reduzida na razão da variável "$scale". O tamanho do thumbnail é calculado "multiplicando-se o tamanho real da imagem pelo valor de $scale".

Usando esses valores de largura e altura, criamos uma nova imagem temporária usando a função "imagecreatetruecolor()". Se você não estiver usando GD 2.0+, use a função "imagecreate()" (os seus jpegs podem ficar medonhos, use 2.0+).

A imagem original vai agora ser redimensionada para o tamanho da temporária. Podemos então liberar o arquivo da imagem e renomear a temporária para "$img".

<?php
// Cria uma imagem de erro se necessário
if (!$img) {
    $img = imagecreate(MAX_WIDTH, MAX_HEIGHT);
    imagecolorallocate($img, 204, 204, 204);
    $c = imagecolorallocate($img, 153, 153, 153);
    $c1 = imagecolorallocate($img, 0, 0, 0);
    imageline($img, 0, 0, MAX_WIDTH, MAX_HEIGHT, $c);
    imageline($img, MAX_WIDTH, 0, 0, MAX_HEIGHT, $c);
    imagestring($img, 2, 12, 55, 'erro ao carregar imagem', $c1);
}
?>

Se a extensão não for reconhecida ou o arquivo não for encontrado, uma imagem de erro será gerada. Ela será um quadrado cinza com um X cinza o cortando e a frase 'erro ao carregar a imagem' em preto, mas também pode ser uma imagem de erro previamente criada.

A função "imagestring" escreve texto na imagem. Os seus parâmetros, apesar de não parecerem fazer sentido algum, são muito fáceis de entender. Em ordem: "$img" é a imagem na qual a string será escrita (handle); "2" é o tamanho da letra, variando de 1 a 5; "12" é a coordenada x da base esquerda da string, enquanto que "55" corresponde à base inferior; 'erro ao carregar a imagem' - surpresa!!! - é a string em si, que será escrita; "$c1" é a cor alocada para s string, definida logo acima, em RGB.

<?php
// Mostra a imagem
header('Content-type: image/jpeg');
imagejpeg($img);
?>

Finalmente, mostra a imagem. Pode ser usado o parâmetro opcional "int(quality)" da função "imagejpeg", para controlar a qualidade da imagem mostrada. O parâmetro varia de 0 a 100, sendo o seu default "75".

E a imagem grande?

Como você pode ver, nem é preciso tanta cafeína assim para gerar thumbnails em PHP. Agora, só falta o gran-finale do artigo: e como linkar as imagens em tamanho natural?

para isso precisaremos de:

  1. uma página listando as imagens de um diretório

  2. algumas imagens maiores que 160x120px

  3. um pouco de xhtml e css



Listando as imagens
A nossa aventura termina com uma página xhtml simples, com código PHP embutido, que lerá as imagens do diretório padrão de imagens e mostrará uma lista de thumbnails.

<!doctype html public "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>.: Lista Imagens :.</title>
<meta name="author" content="mauricio wolff :aka: mauwolff" />

<style type="text/css" media="all">
<!--
body, p { font-family: arial, sans-serif; font-size: 12px; }
image&nbsp;&nbsp;&nbsp;{ border: 0; display: block; clear: both; margin: 15px; text-align: center;}
-->
</style>
</head>

<body>
<?php 
// Abre o diretório atual para leitura de imagens...
if ($handle = opendir('.')) { 
    while (false !== ($file = readdir($handle))) { 
    // Só carrega as imagens que tiverem extensões esperadas
    if (strtolower(substr($file, -4, 4)) == '.jpg' || strtolower(substr($file, -4, 4)) == '.gif' || strtolower(substr($file, -4, 4)) == '.png' || strtolower(substr($file, -5, 5)) == '.jpeg') { 
        $alt = 'clique para ver a imagem no tamanho original...';
        echo '<a href="'.$file.'" target="_blank"><img src="thumb.php?'.$file.'" alt="'.$alt.'" /></a>'."\n"; 
    }
} 
closedir($handle); 
} 
?>
</body>
</html>

Simples, não? O css define a apresentação do xhtml, que somente terá uma lista de thumbnails a mostrar. As imagens automaticamente não terão borda alguma, estarão centralizadas na página e serão listadas uma abaixo a outra. Com CSS isso é facilmente alterável, sem mexer no html ou usar gambiarras como "&lt;br /&gt;".

O código php somente abre o diretório atual, lista todos os arquivos do tipo desejado e monta os links para as imagens grandes. Se você criar um arquivo de texto vazio e renomeá-lo para "teste.gif", verá a imagem de erro.

Para você ter uma idéia da economia: um arquivo de "18.809 bytes" ficou com "2.941 bytes", e outro de "287.005 bytes" ficou com "3.257 bytes" (1,13% do tamanho).


Conclusão

Claro que, normalmente, é melhor usar uma classe de template, deixar o seu código mais modular, não misturar apresentação e lógica... mas este é um exemplo funcional do que se pode fazer com meia-dúzia de linhas e algumas fotos...

Bom... já foi um exercício. Agora aproveite os exemplos, refine um pouco o código e, depois de botar as fotos da festa no ar, volte pra cama. Aproveite a ressaca...

base64: SGFwcHkgY29kaW5nIQ== 

Sobre o autor
Mauricio Wolff é o fundador, criador, programador e fazedor de cafézinho da <a href="http://www.organiKa.com.br">organiKa.com.br</a>. Gosta de PHP, XML, CSS, Sânscrito, Vedanta, Cinema, Artes, Kung-Fu, Física e História. Sim, ele gosta de bastante coisa... é casado e tem uma filha.

Comentários:

Mostrando 1 - 10 de 47 comentários
olá pessoal sou novo no php
como eu sei se meu PHP foi compilado com suporte a GD 2.0.1 e que ele está sendo carregado no PHP.INI
24/06/2013 8:09pm (~11 anos atrás)

Edson Correa disse:
Testei e está funcionando, exceto com imagens maiores. Tenho um sistema onde o cliente publica fotos direto da câmera digital e às vezes as fotos estão em 2000 pixels ou mais de largura. Essas imagens parecem não funcionar no script.

Vocês sabem se é algum erro do script ou é alguma limitação do GD2 ou mesmo do servidor?
14/03/2008 11:59am (~16 anos atrás)

cara tudo correu certinho, só tem um probleminha, quando clico na imagem, ela não redireciona para o endereço correto ocasionando o seguinte erro:
Objeto não encontrado!

A URL requisitada não foi encontrada neste servidor. O link na página referida parece estar com algum erro ou desatualizado. Por favor informe o autor desta página sobre o erro.

Se você acredita ter encontrado um problema no servidor, por favor entre em contato com o webmaster.
Error 404
localhost
03/26/07 12:58:24
Apache/2.2.2 (Win32) DAV/2 mod_ssl/2.2.2 OpenSSL/0.9.8b mod_autoindex_color PHP/5.1.4

se alguém puder me ajudar agradeço.

mais só em fazer os thumbs já fiquei satisfeito ótimo tuto, valeu mesmo.
26/03/2007 8:59am (~17 anos atrás)

Gostaria de centralizar a imagem nesse thumb. Não estou conseguindo.
14/03/2007 1:06pm (~17 anos atrás)

jorge lopes disse:
pessoal, gostaria de tirar uma dívida, as imagens irão aparecer uma embaixo da outra? se sim, é possível colocar em coluna de 4 por exemplo?
30/10/2006 2:04am (~18 anos atrás)

Ebek Diogo disse:
Ei realmente valeu a dica, estou começando no php espero que encontr ajuda com todos vcs.
12/07/2006 4:58pm (~18 anos atrás)

Léo Borges disse:
bom artigo, havia feito algo semelhante. o problema é que o usuário fica sem o thumbnail em cache, ou seja: toda vez que ele voltar para a pagina, o browser carrega o thumbnail novamente.
08/06/2006 3:13pm (~18 anos atrás)

refiz o código, por favor baixe as novas versões em:

http://www.mauwolff.com.br/php/thumb2.phps
http://www.mauwolff.com.br/php/index_thumb2.phps

e podes testar em:
http://www.mauwolff.com.br/php/index_thumb2.php

Um dia posto aqui a classe que uso atualmente. Mas para aprendizado, acho que é melhor entender o que a classe faz, assim vc pode criar a sua própria.

Grato!

Mauricio Wolff
01/06/2006 5:33am (~18 anos atrás)

Tenho uma classe publicada que faz o trabalho completo de redimensionamento (geração de thumbnails) para JPG, PNG e/ou GIF: http://www.phpbrasil.com/scripts/script.php/id/1451
05/03/2006 12:59pm (~18 anos atrás)

Hugo Nogueira disse:
Para ficar com maior qualidade, use: imagecopyresampled(); no lugar de imagecopyresized();.

Abraços...

hugo@hugonogueira.com
03/03/2006 12:51pm (~19 anos atrás)

Novo Comentário:

(Você pode usar tags como <b>, <i> ou <code>. URLs serão convertidas para links automaticamente.)