Conception d'une galerie d'images générée à la volée
Date de publication : 15 Septembre 2006 , Date de mise à jour : 15 Septembre 2006
Par
Pierre-Baptiste Naigeon (Tutos, tests et articles web)
Comment construire une application capable de lister toutes les images (gif, jpg, png) d'un répertoire (et de ses sous-répertoires), de générer des miniatures de ces images si elles n'existent pas encore, puis d'afficher les miniatures avec un lien sur l'image originale.
I. Introduction
I-A. Précisions et pré-requis
I-B. Détail des opérations à effectuer
II. Travail préparatoire
II-A. Parcours du répertoire
II-B. Test du format de fichier
III. Génération de la galerie
III-A. Création des miniatures
III-B. Création du tableau de liens
III-C. Affichage des images
IV. Conclusion
I. Introduction
I-A. Précisions et pré-requis
Dans ce tutorial, je n'ai pas souhaité passer par une base de données, afin que le code soit facilement exploitable. Cela aurait pu permettre une gestion plus fine des miniatures (tests de mise à jour, etc.), mais je n'ai pas retenu cette solution.
Nous n'utiliserons pas non plus de système de template, toujours dans un but de simplicité et de facilité de déploiement.
Mes fonctions et styles seront tous dans un seul et unique fichier, afin de faciliter encore plus le déploiement.
Je veux simplement déposer mon fichier PHP à la racine d'un répertoire, et qu'il se débrouille tout seul :)
Pour aborder ce tutorial, un minimum de connaissances en PHP vous sera demandé.
Les différents traitements de l'image s'effectuent à l'aide le la librairie GD2. Il faut donc penser à l'activer sur son serveur web.
I-B. Détail des opérations à effectuer
Essayons de penser en terme de programmation : quelles seront les étapes à effectuer pour en arriver au résultat souhaité ?
Voici celles que j'ai retenues :
- Lister tous les fichiers du répertoire et des sous-répertoires.
- Pour chaque fichier, vérifier s'il est de type image et correspondant à l'un des trois formats retenus (gif, jpg et png).
- Pour chaque image, vérifier si la miniature existe déjà ou pas (si elle existe, elle portera le même nom que l'original, mais sera située dans un répertoire 'miniature' situé immédiatement sous son original).
- Si la miniature n'existe pas, il va falloir la générer (et créer le répertoire 'miniature' s'il n'existe pas, mais c'est du détail).
- Générer le lien de la miniature vers son original dans un tableau global.
- Et enfin, une fois tous les liens générés, afficher la galerie (je serai très sommaire sur cette partie, chacun l'accordera à sa sauce... ).
II. Travail préparatoire
Passons maintenant au code de l'application, étape par étape, en suivant l'ordre donné ci-dessus.
Définissons simplement la structure de notre page avant toute chose :
| Structure générale de la page |
<?php
error_reporting(E_ALL | E_STRICT);
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Affichage images</title>
<style type="text/css">
<!--
a img {
border-color:transparent;
}
-->
</style>
</head>
<body>
<?php
liste_repertoire('.');
affichage();
?>
</body>
</html> |
Comme vous le voyez, nous avons fait appel à deux fonctions seulement dans le corps de notre page (liste_repertoire et affichage). Il ne nous reste plus maintenant qu'à les définir petit à petit.
A partir de maintenant, tout le code sera à rajouter tout en haut du fichier précédemment crée, entre les balises <?php et ?>, sous la commande "error_reporting(E_ALL | E_STRICT);".
II-A. Parcours du répertoire
Tâchons dès maintenant d'éviter les écueils. Lors du listing d'un répertoire, PHP nous retournera, outre les fichiers/dossiers visibles via l'explorateur, les dossiers "." et "..". Il faudra prendre garde à ne pas les traiter.
Rappelons-nous que les miniatures seront stoquées dans un sous-répertoire 'miniature' du dossier courant. Il faudrait veiller à ne pas le traiter non plus, afin d'éviter de se retrouver avec des miniatures de miniatures de miniatures....
Définissons donc un tableau contenant ces restrictions : $tabl_exclus.
Voici une fonction basique permettant de lister les fichiers d'un répertoire et de ses sous répertoires, et tenant compte des remarques ci-dessus.
| Lister les fichiers d'un répertoire |
$tabl_exclus = array ('.', '..', 'miniature');
function liste_repertoire($dir) {
if ($handle = opendir($dir)) {
while (($file = readdir($handle)) !== false) {
$chemin_fichier = $dir.'/'.$file;
if (is_dir($chemin_fichier)) {
if (!in_array($file, $GLOBALS['tabl_exclus'])) {
liste_repertoire($dir.'/'.$file);
}
} else {
$chemin_miniature = $dir.'/miniature/'.$file;
}
}
closedir($handle);
}
} |
Voici la traduction en langage naturel du code ci-dessus :
Si on arrive à ouvrir le répertoire passé en paramètre
Tant qu'il y a des choses à lire dans ce répertoire
On génère le chemin complet du fichier
Si le fichier est un répertoire
Si le répertoire n'est pas exclus
On rappelle la fonction avec le nouveau répertoire en paramètre
Sinon
Si le fichier est une image
On crée le chemin de la miniature
Si la miniature n'existe pas
On génère la miniature
Fin si
On crée le lien
Fin si
Fin si
Fin si
Fin tant que
Fin si |
Comme vous le voyez, rien de bien sorcier ici.
Il nous reste donc à définir une fonction de test du format de fichier, une de test d'existence de la miniature, une de génération, et une de création du lien.
II-B. Test du format de fichier
Concentrons-nous maintenant sur le test du format de fichier.
J'ai déclaré que seuls trois types de fichiers nous intéressent : gif, jpg et png. Nous allons donc définir un tableau qui contiendra les types MIME de ces fichiers.
De plus, comme dans cette fonction nous allons récupérer des informations qui nous serviront plus tard, nous allons définir trois variables globales : largeur, hauteur, et extension.
| Vérification du format de fichier |
function est_image($chemin_fichier) {
if (list($GLOBALS['largeur'], $GLOBALS['hauteur'], $type) = getimagesize($chemin_fichier)) {
$type = image_type_to_mime_type($type);
if (in_array($type, $GLOBALS['types_ok'])) {
$ext = explode("/", $type);
$GLOBALS['extension'] = $ext[1];
return true;
}
}
return false;
} |
Cette fonction renvoie TRUE si le fichier est une image qui correspondant à nos critères, FALSE sinon.
Traduit en langage naturel, voici ce que fait cette fonction :
Si le fichier est une image
On récupère son type MIME
Si son type correspond à nos attentes
On récupère son extension
On renvoie TRUE
Fin si
Fin si
On renvoie FALSE |
Ca a l'air tellement plus facile vu comme ça ;). Mais ne vous inquiétez pas, ce n'est pas si compliqué que ça y parait.
La seule difficulté réside dans l'utilisation de différentes fonctions pas forcément connues :
- getimagesize : Renvoie des informations sur un fichier image. Si le fichier n'est pas une image, renvoie FALSE.
- image_type_to_mime_type : Récupère le type MIME d'une image.
- explode : découpe une chaîne en un tableau, suivant de séparateur défini (ici, le '/').
Maintenant que nous savons si le fichier nous intéresse ou non, penchons-nous sur la génération de la miniature.
III. Génération de la galerie
III-A. Création des miniatures
Avant toute chose, nous allons tester si la miniature existe ou non. Si elle existe déjà, nous ne prendrons même pas la peine d'effectuer la moindre vérification, vous êtes censés être organisé.
| Test d'existence de la miniature |
if (!file_exists($chemin_miniature)) {
} |
Pas besoin d'explications pour ce test je pense.
Passons maintenant à la génération proprement dite des miniatures. Attention, ça se corse un peu :
| Génération de la miniature |
function genere_miniature($dir, $chemin_image, $chemin_miniature) {
$taille_max = 100;
if ($GLOBALS['largeur'] <= $GLOBALS['hauteur']) {
$ratio = $GLOBALS['hauteur'] / $taille_max;
} else {
$ratio = $GLOBALS['largeur'] / $taille_max;
}
$larg_miniature = $GLOBALS['largeur'] / $ratio;
$haut_miniature = $GLOBALS['hauteur'] / $ratio;
$destination = imagecreatetruecolor($larg_miniature, $haut_miniature);
$source = call_user_func('imagecreatefrom'.$GLOBALS['extension'], $chemin_image);
imagecopyresampled( $destination,
$source,
0, 0, 0, 0,
$larg_miniature,
$haut_miniature,
$GLOBALS['largeur'],
$GLOBALS['hauteur']);
if (!is_dir($dir.'/miniature')) {
mkdir ($dir.'/miniature', 0700);
}
call_user_func('image'.$GLOBALS['extension'], $destination, $chemin_miniature);
imagedestroy($destination);
imagedestroy($source);
} |
Commençons par jeter un oeil sur deux lignes fort étranges :
$source = call_user_func('imagecreatefrom'.$GLOBALS['extension'], $chemin_image); |
Qu'est-ce donc ?
Et bien cela correspond exactement au code suivant :
Plus élégant tout de même, non ?
Il en va de même pour la ligne suivante :
call_user_func('image'.$GLOBALS['extension'], $destination, $chemin_miniature); |
Qui correspond à :
switch ($GLOBALS['extension']) {
case 'jpeg':
$source = imagejpeg($destination, $chemin_miniature);
break;
case 'gif':
$source = imagegif($destination, $chemin_miniature);
break;
case 'png':
$source = imagepng($destination, $chemin_miniature);
break;
} |
Maintenant, une petite explication des différentes fonctions de manipulation d'image utilisées :
Ca y est, le plus difficile est passé. Il ne reste maintenant plus que quelques broutilles à gérer. Créons donc maintenant le tableau de lien, avec la miniature fraîchement crée.
III-B. Création du tableau de liens
Courage, la fin est proche ...
| Ajout du lien au tableau global |
function ajoute_lien($chemin_image, $chemin_miniature, $file) {
$taille_html_miniature = getimagesize($chemin_miniature);
$taille_html_miniature = $taille_html_miniature[3];
$lien = '<a href="'.$chemin_image.'">';
$lien .= '<img src="'.$chemin_miniature.'" '.$taille_html_miniature.' alt="'.$file.'">';
$lien .= '</a>'."\n";
array_push($GLOBALS['tabl_liens'], $lien);
} |
La simplicité même. Nous nous contentons de récupérer les attributs de taille de la miniature, puis de créer un lien de la miniature vers l'image originale, et d'insérer le tout dans un tableau global.
Passons à la dernière étape de notre voyage, l'affichage de notre galerie.
III-C. Affichage des images
Je vous avais prévenus, vous n'allez pas être déçus... Voici ma fonction simpliste d'affichage de la galerie.
Il vous est cependant possible de l'adapter à vos besoins, sans avoir à retoucher au reste des fonctions...
| Affichage de la galerie |
define ("NBRE_COLONNES", 4);
function affichage() {
$compteur = 1;
foreach ($GLOBALS['tabl_liens'] as $val_lien) {
if ($compteur % NBRE_COLONNES == 1) {
echo '<br>';
}
echo $val_lien;
$compteur++;
}
} |
J'ai simplement défini la constante NBRE_COLONNES pour mon affichage. Je me contente toute les quatre images d'afficher un retour à la ligne.
IV. Conclusion
Voici donc votre fichier tel qu'il devrait de présenter au final :
| Fichier final |
<?php
error_reporting(E_ALL | E_STRICT);
define ("NBRE_COLONNES", 4);
$types_ok = array ('image/jpeg', 'image/gif', 'image/png');
$tabl_exclus = array ('.', '..', 'miniature');
$tabl_liens = array();
function liste_repertoire($dir) {
if ($handle = opendir($dir)) {
while (($file = readdir($handle)) !== false) {
$chemin_fichier = $dir.'/'.$file;
if (is_dir($chemin_fichier)) {
if (!in_array($file, $GLOBALS['tabl_exclus'])) {
liste_repertoire($dir.'/'.$file);
}
} else {
if (est_image($chemin_fichier)) {
$chemin_miniature = $dir.'/miniature/'.$file;
if (!file_exists($chemin_miniature)) {
genere_miniature($dir, $chemin_fichier, $chemin_miniature);
}
ajoute_lien($chemin_fichier, $chemin_miniature, $file);
}
}
}
closedir($handle);
}
}
function est_image($chemin_fichier) {
if (list($GLOBALS['largeur'], $GLOBALS['hauteur'], $type) = getimagesize($chemin_fichier)) {
$type = image_type_to_mime_type($type);
if (in_array($type, $GLOBALS['types_ok'])) {
$ext = explode("/", $type);
$GLOBALS['extension'] = $ext[1];
return true;
}
}
return false;
}
function genere_miniature($dir, $chemin_image, $chemin_miniature) {
$taille_max = 100;
if ($GLOBALS['largeur'] <= $GLOBALS['hauteur']) {
$ratio = $GLOBALS['hauteur'] / $taille_max;
} else {
$ratio = $GLOBALS['largeur'] / $taille_max;
}
$larg_miniature = $GLOBALS['largeur'] / $ratio;
$haut_miniature = $GLOBALS['hauteur'] / $ratio;
$destination = imagecreatetruecolor($larg_miniature, $haut_miniature);
$source = call_user_func('imagecreatefrom'.$GLOBALS['extension'], $chemin_image);
imagecopyresampled( $destination,
$source,
0, 0, 0, 0,
$larg_miniature,
$haut_miniature,
$GLOBALS['largeur'],
$GLOBALS['hauteur']);
if (!is_dir($dir.'/miniature')) {
mkdir ($dir.'/miniature', 0700);
}
call_user_func('image'.$GLOBALS['extension'], $destination, $chemin_miniature);
imagedestroy($destination);
imagedestroy($source);
}
function ajoute_lien($chemin_image, $chemin_miniature, $file) {
$taille_html_miniature = getimagesize($chemin_miniature);
$taille_html_miniature = $taille_html_miniature[3];
$lien = '<a href="'.$chemin_image.'">';
$lien .= '<img src="'.$chemin_miniature.'" '.$taille_html_miniature.' alt="'.$file.'">';
$lien .= '</a>'."\n";
array_push($GLOBALS['tabl_liens'], $lien);
}
function affichage() {
$compteur = 1;
foreach ($GLOBALS['tabl_liens'] as $val_lien) {
if ($compteur % NBRE_COLONNES == 1) {
echo '<br>';
}
echo $val_lien;
$compteur++;
}
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Affichage images</title>
<style type="text/css">
<!--
a img {
border-color:transparent;
}
-->
</style>
</head>
<body>
<?php
liste_repertoire('.');
affichage();
?>
</body>
</html> |
 |
Lors des premières générations de la galerie, si vous avez beaucoup d'images dans votre répertoire (et ses sous-répertoires), il est tout à fait possible que le script tombe en timeout, à cause de la durée de génération des miniatures.
Pas d'inquiétudes ! Il vous suffit de relancer le script, et il reprendra là où il s'était arrété.
Vous pouvez également aller modifier les paramètres de votre serveur web afin d'autoriser une exécution plus longue (max_execution_time dans php.ini).
|
Comme vous avez pu le voir tout du long de cet article, rien de très compliqué n'a été effectué.
Il est aisé avec la connaissance des bonnes fonctions, ainsi qu'un temps de réflexion préalable, de créer de telles applications.


Les sources présentées sur cette page sont libres de droits,
et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation
constitue une oeuvre intellectuelle protégée par les droits d'auteurs. Copyright ©
2006 Pierre-Baptiste Naigeon. Aucune reproduction,
même partielle, ne peut être faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc sans l'autorisation expresse de l'auteur.
Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E
de dommages et intérêts.
Cette page est déposée à la
SACD.