WRInaute accro
Pour les sites qui ont beaucoup d'images à gérer et ou qui sont amenés a les afficher dans des tailles variables, il est évident que la gestion de miniature et de cache deviens un point important.
Je me suis penché sur ce problème car j'ai quelques 20 000 images a afficher dans des tailles variables (4 au moins en plus de l'original) et le serveur qui est affecté a se travail commençait a montrer des signes de faiblesse.
Ma première démarche (qui date déjà de quelques années) a été de produire des images a la taille voulue a la demande. Ne souhaitant pas pour des raisons de volume conserver ces images sur le serveur le parti pris fut de la produire a la volée avec la bibliothèque GD.
Centralisation des requêtes
Le premier point était donc de centraliser ces images sur un script capable de générer cela voici la technique utilisée qui servira par la suite au diverses solutions proposées :
Dans un premier temps donc il conviens d'intercepter toutes les demande d'image retaillées via une règle de réécriture :
Dans le htaccess :
Le principe est simple toute URL contenant "thumb" est intercepté par la règle, ce qui suis "thumb" est envoyé comme paramètre (en GET) au script "images.php"
Si votre URL de miniature se présente sous la forme :
-http://www.example.com/images/thumb/250/dossier/photo.jpg
le script images.php recevra comme paramètre "data" la valeur "/250/dossier/photo.jpg".
Si "/dossier/photo.jpg" correspond a un chemin physique vers votre photo le script sera en plus en mesure de manipuler la photo physique d'origine pour y appliquer différents traitements que nous allons voir ci dessous.
Première approche basique
Le script de miniature recevant toutes les requêtes http il conviens donc de les satisfaire, pour cela la première démarche du script "images.php" sera de comprendre ce qu'on lui demande a savoir d'être en mesure de connaitre la photo demandée et la taille désirée.
Nous avons vu que le paramètre reçu se présentait sous la forme "/250/dossier/photo.jpg". 250 représente la largeur de l'image, "/dossier/photo.jpg" le chemin du fichier. J'ai été tenté d'utiliser des expressions régulières pour extraire les deux données utiles, je l'ai même fait longtemps mais c'est très gourmand en ressource j'ai donc changé pour un simple parcours de la variable dans une boucle ce qui fait très bien l'affaire a moindre frais au final voici le code :
La variable GET est lue en intégralité depuis la position 1 (on évite ainsi le slash de démarrage) son contenu lettre a lettre est stocké dans $format tant qu'on ne rencontre pas un autre slash indiquant que c'est le chemin qui est en train d'être lu, on change alors de variable de stockage ($fichier) en utilisant une bascule ($comut)
A partir de là le script sais ce qu'il doit faire :
1/ envoyer les entêtes
2/ évaluer les dimensions de l'image a générer
3/ créer l'image avec GD
4/ livrer l'image a l'usager
Notez au passage la gestion des entête qui définissent un temps d'expiration important pour cette ressource qui est censée être statique.
Les principaux soucis que j'ai rencontré en deux ans avec cette solution sont :
1/ la relative faible qualité des images produites, en effet Si GD est une très bonne bibliothèque très bien intégrée à php et largement diffusée, la sortie n'est pas toujours de très bonne qualité surtout pour des tailles assez faibles.
2/ GD est aussi gourmand en ressources ...
Voici un résultat comparatif avec GD a droite et l'original traité avec gimp à gauche pour 250px de large :
GD produit une image acceptable mais avec des marches d'escalier et des tons très froids "taillés à la serpe".
Seconde approche optimisée
Dans une seconde approche je me suis penché sur la bibliothèque ImageMagik. ImageMagick n'est malheureusement pas aussi répandue que GD sur les serveurs web, si vous avez un dédié ce n'est pas un souci en revanche si vous travaillez sur un mutu c'est parfois plus délicat quoi qu'il en
soit, si les mutu ne donne pas accès a ImageMagick via php c'est bien rare que la bibliothèque n'existe pas sur le serveur et c'est pourquoi nous utiliserons la commande exec() pour y accéder.
A partir de là, plusieurs facteurs ont retenus mon attention :
1/ la qualité de l'image produite.
2/ la "vélocité du script" (donc la charge php infligée au serveur)
3/ la résultante des deux précédents donc le temps de chargement de l'image.
Voici la partie script lié purement a la fabrication de l'image :
Je ne reviens pas sur l'extraction des données ni sur l'envoie du header, c'est pareil mais je donne quelques explications sur la commande exécutée via exec() :
convert ${fichier} -thumbnail ${newwidth}x${newheight} -quality 50 -strip miniature.jpg
Le script fait donc appel a la commande "convert" pour produire une image de taille ${newwidth}x${newheight} en qualité 50 (plus le chiffre est bas plus la qualité baisse), "strip" quand a lui vire les données exif diverses (de mémoire) afin d'alléger le poids de l'image le résultat est envoyé dans le fichier "miniature.jpg" sur le disque.
file_get_contents() lira le fichier pour ne faire un écho et comme les entêtes on été envoyé, le client recevra son image. J'utilise ici file_get_contents() car je n'ai pas trouvé le moyen d'envoyer directement sur la sortie standard de php le contenu via la ligne de commande. Nous verrons par la suite que cet inconvénient va se transformer en avantage.
Le résultat est, malgré la faible qualité demandée (afin d'économiser de la ressource), déjà bien supérieur a ce que produisais GD même référence de comparaison que précédemment :
la comparaison des deux bibliothèques en vis a vis de passe de tous commentaire :
Pour que la comparaison soit a peut prêt valable, il faut noter le poids physique de chaques images. GD fourni une image de 30,50Ko, avec les réglages choisis, ImageMagick produit une image don le poids est de 28,44Ko. A ce stade nous avons gagné sur deux points importants :
1/ la taille du fichier donc (temps de chargement, économie de Bande passante, ....)
2/ qualité picturale de l'image.
Que pouvons nous encore gagner ?
Mise en cache
Le gros souci des scripts de génération de miniature a la volée est en fait tout entier dans la ressource processeur utilisée pour la créations des images. D'une part ça mobilise php pour des taches peut enviable, ensuite ça peut vite mettre a genoux la mémoire si on brasse de grosse tailles. Que dire sinon que cette consommation se répète a chaque chargement d'une photo ...
Malgré les headers adaptés et la gestion du cache des navigateurs vous ne gagnerez pas grand chose niveau serveur si un internaute décide de visualiser beaucoup de vos photos ...
Principal inconvénient tout a l'heure, le fichier généré qu'il fallait relire pour l'envoyer vers la sortie va nous servir pour un cache intelligent ...
Un cache intelligent est pour moi un système qu'on va pouvoir manipuler pour en faire autre chose (on verra plus loin), qui est économe si possible en ressource (je part du principe que la taille des disques n'est pas un souci) et qui (normal) nous fait gagner du temps et du calcul.
Voici donc la modification du script précédent qui introduit la notion de mise en cache des miniature :
Le système s'articule autour d'une première condition évidente, le fichier de cache existe t il ?
1/ si non (cas else) on va générer l'image et créer e fichier de cache en la rangeant dedans
2/ si oui il faudra recourir a un second test pour savoir si l'image est dedans. En effet le cache est commun a toutes les tailles d'une photo donc pour chaque photo on aura un fichier de cache qui contiendra toutes les tailles voulues.
Le code qui gère la création du cache avec un fopen crée un fichier php (qu'on peut donc inclure très important !, mais aussi downloader, ....) qui contiens un simple ligne :
Vous le voyez, le cache est un "script" qui assigne le contenu de l'image encodé base 64 a un variable de tableau portant le nom de l'image et sa taille en coordonnée (notez le base 64 on va y revenir :wink: ).
Dans le cas ou une photo serait demandée ailleurs avec une autre dimension une nouvelle variable de tableau serait générée pour contenir l'autre dimension dans le même fichier de cache :
Un unique fichier de cache peux donc contenir toutes les photos en différentes tailles.
Nous avions vu qu'un écho envoyait la photo demandé sur la sortie standard, là avec le dispositif de cache un include nous permet de savoir si la variable correspondant a l'image existe si oui on en fait un echo après avoir décodé son contenu ( echo base64_decode($photo[$ident][$format]) ) si non on va générer l'image, l'ajouter au fichier de cache (fopen() en mode appends) et faire un echo.
Astuce du Base 64
tant qu'on en est dans l'optimisation il faut parler de la technique qui consiste a encoder les photos directement dans le contenu de la page. Disons que ça ne fait pas gagner de volume, mais que ça peut considérablement diminuer le nombre de requêtes fait par le navigateur de l'internaute, qui au lieu de charger le code html de la page et ensuite les photos incluses va recevoir une page plus grosse encodée en html qui contiens le contenu des images.
Je vous conseille la lecture de Data uri schema pour comprendre là ou ça peut être utile et là ou ça ne l'est pas. Il est évident que ça purra aider mais pas toujours. Quoi qu'il en soit si vous devez passer une image au navigateur avec cette technique l'inclusion du fichier cache le permettra facilement puisque au final toutes les datas base 64 seront déjà dedans.
Les perfs au final
Toujours sur la base d'images "similaires" il conviens au final de se faire une idée précise des gains pour le serveur.
J'estime que pour qu'un script de miniature soit estimé correct, il dois s'approcher au plus près des performances d'une image physique présente sur le disque. Voici un comparatif :
Les portions d'images ci dessus sont issues de firefox et de son outil de débogage, je me suis concentré sur les accès réseau pour voir les différences. Le test est fait avec CRTL +F5 afin d'obliger le rechargement complet plus la résolution DNS.
On vois facilement que le temps de réception est assez similaire, normal le débit réseau pouvant être assimilé a une constante des images de poids similaire vont transiter pendant le même temps, idem pour les temps de résolution et de connexion qui sont propre a l'accès au serveur et pas au script.
Là ou ça deviens intéressant c'est qu'IMageMagick nous a fait gagner du temps de calcul (ce qui se passe durant l'attente) 71ms pour ImageMagick alors que GD prend 95ms pour le même travail.
On peut se poser la question de savoir a quoi correspond le temps d'attente pour l'image physique (41ms) je n'ai aps trouvé cette réponse du moins pas satisfaisante quoi qu'il en soit elle interviens a titre comparatif comme une valeur absolue sous laquelle il est impossible de descendre puisque a priori il n'y a aucun traitement de fait par php.
Si on en restait là, hormis le facteur qualité pictographique, les gains apportés par imageMagick ne serait pas affolants ou transcendants observons donc le même test sur une image plus importante (900px de large) :
Je ne reviens pas sur la résolution DNS, la connexion etc ... mais si on ce concentre sur l'attente on constate là un gain effectif relativement important puisque ImageMagick prend presque moitié moins de temps pour travailler (avec mise en cache et encodage base64 en plus ...) et rivalise encore bien avec l'image physique qui elle demande toujours le même temps d'attente (41 / 43 ms dans les deux cas) tout comme la version Imagemagick en cache.
Conclusions
1/ Le temps de traitement de l'image deviens donc grâce au cache une "constante".
2/ La qualité de l'image est amélioré par le choix de la bonne bibliothèque.
3/ 10 tailles d'image n'occupent qu'un unique fichier et pas 10.
4/ la gestion des images passe par un seul script.
Cerise sur le sunday
Vue les préoccupations générales quand au droits d'auteur un petit watermark s'imposait :
Voici le principe que j'ai adopté pour rester dans la ligne du reste du code.
Ici photos en 250px, 125px, 900px (extrait)
Le script
Ce script est (chez moi) dans un dossier physique "/images" les photos a gérer sont dans des sous dossier de "/images".
Il manque :
* La destruction de la miniature physique crée (après la mise en cache) $fichierDisque.
* La gestion du nom des fichiers cache ('cacheFile.php'), je vous laisse plancher la dessus c'est pas compliqué à adapter.
Si vous avez des idées d'optimisation je suis preneur.
Je me suis penché sur ce problème car j'ai quelques 20 000 images a afficher dans des tailles variables (4 au moins en plus de l'original) et le serveur qui est affecté a se travail commençait a montrer des signes de faiblesse.
Ma première démarche (qui date déjà de quelques années) a été de produire des images a la taille voulue a la demande. Ne souhaitant pas pour des raisons de volume conserver ces images sur le serveur le parti pris fut de la produire a la volée avec la bibliothèque GD.
Centralisation des requêtes
Le premier point était donc de centraliser ces images sur un script capable de générer cela voici la technique utilisée qui servira par la suite au diverses solutions proposées :
Dans un premier temps donc il conviens d'intercepter toutes les demande d'image retaillées via une règle de réécriture :
Dans le htaccess :
Code:
# traitement des miniatures
RewriteCond %{REQUEST_URI} ^(.*)thumb.*
RewriteRule ^(.*)thumb(.*)$ /images/images.php?data=$2 [L]
Le principe est simple toute URL contenant "thumb" est intercepté par la règle, ce qui suis "thumb" est envoyé comme paramètre (en GET) au script "images.php"
Si votre URL de miniature se présente sous la forme :
-http://www.example.com/images/thumb/250/dossier/photo.jpg
le script images.php recevra comme paramètre "data" la valeur "/250/dossier/photo.jpg".
Si "/dossier/photo.jpg" correspond a un chemin physique vers votre photo le script sera en plus en mesure de manipuler la photo physique d'origine pour y appliquer différents traitements que nous allons voir ci dessous.
Première approche basique
Le script de miniature recevant toutes les requêtes http il conviens donc de les satisfaire, pour cela la première démarche du script "images.php" sera de comprendre ce qu'on lui demande a savoir d'être en mesure de connaitre la photo demandée et la taille désirée.
Nous avons vu que le paramètre reçu se présentait sous la forme "/250/dossier/photo.jpg". 250 représente la largeur de l'image, "/dossier/photo.jpg" le chemin du fichier. J'ai été tenté d'utiliser des expressions régulières pour extraire les deux données utiles, je l'ai même fait longtemps mais c'est très gourmand en ressource j'ai donc changé pour un simple parcours de la variable dans une boucle ce qui fait très bien l'affaire a moindre frais au final voici le code :
PHP:
<span class="syntaxdefault"> $comut </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> true</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> for</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">;</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword"><</span><span class="syntaxdefault">strlen</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'data'</span><span class="syntaxkeyword">]);</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">++){<br /></span><span class="syntaxdefault"> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$comut</span><span class="syntaxkeyword">){<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// on a pas trouvé le slash<br /></span><span class="syntaxdefault"> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'data'</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">]==</span><span class="syntaxstring">'/'</span><span class="syntaxkeyword">){<br /></span><span class="syntaxdefault"> $comut </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> false</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}else{<br /></span><span class="syntaxdefault"> $format </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $format</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'data'</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">];<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}else{<br /></span><span class="syntaxdefault"> $fichier </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $fichier</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'data'</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">];<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}</span><span class="syntaxdefault"> </span>
A partir de là le script sais ce qu'il doit faire :
1/ envoyer les entêtes
2/ évaluer les dimensions de l'image a générer
3/ créer l'image avec GD
4/ livrer l'image a l'usager
PHP:
<span class="syntaxdefault"> $fichier </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $_SERVER</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'DOCUMENT_ROOT'</span><span class="syntaxkeyword">].</span><span class="syntaxstring">'/images/'</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$fichier</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> <br /> </span><span class="syntaxcomment">// header php<br /></span><span class="syntaxdefault"> $offset </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> 290304000</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $expire </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> gmdate </span><span class="syntaxkeyword">(</span><span class="syntaxstring">"D, d M Y H:i:s"</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> time</span><span class="syntaxkeyword">()</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">+</span><span class="syntaxdefault"> $offset</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">.</span><span class="syntaxdefault"> </span><span class="syntaxstring">" GMT"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"Last-Modified: "</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">.</span><span class="syntaxdefault"> gmdate</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"D, d M Y H:i:s"</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">.</span><span class="syntaxdefault"> </span><span class="syntaxstring">" GMT"</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"Cache-Control: max-age=$offset, public"</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"expires: "</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$expire</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'Content-Type: image/jpeg'</span><span class="syntaxkeyword">);<br /><br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// Calcul des nouvelles dimensions<br /></span><span class="syntaxdefault"> list</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$width</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $height</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> getimagesize</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fichier</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $facteur </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $width </span><span class="syntaxkeyword">/</span><span class="syntaxdefault"> $format</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $newwidth </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $width </span><span class="syntaxkeyword">/</span><span class="syntaxdefault"> $facteur</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $newheight </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $height </span><span class="syntaxkeyword">/</span><span class="syntaxdefault"> $facteur</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> <br /> </span><span class="syntaxcomment">// Chargement<br /></span><span class="syntaxdefault"> $thumb </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> imagecreatetruecolor</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$newwidth</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $newheight</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $source </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> imagecreatefromjpeg</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fichier</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> <br /> </span><span class="syntaxcomment">// Redimensionnement<br /></span><span class="syntaxdefault"> imagecopyresized</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$thumb</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $source</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 0</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 0</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 0</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 0</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $newwidth</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $newheight</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $width</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $height</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> <br /> </span><span class="syntaxcomment">// Affichage<br /></span><span class="syntaxdefault"> imagejpeg</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$thumb</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> </span>
Notez au passage la gestion des entête qui définissent un temps d'expiration important pour cette ressource qui est censée être statique.
Les principaux soucis que j'ai rencontré en deux ans avec cette solution sont :
1/ la relative faible qualité des images produites, en effet Si GD est une très bonne bibliothèque très bien intégrée à php et largement diffusée, la sortie n'est pas toujours de très bonne qualité surtout pour des tailles assez faibles.
2/ GD est aussi gourmand en ressources ...
Voici un résultat comparatif avec GD a droite et l'original traité avec gimp à gauche pour 250px de large :

GD produit une image acceptable mais avec des marches d'escalier et des tons très froids "taillés à la serpe".
Seconde approche optimisée
Dans une seconde approche je me suis penché sur la bibliothèque ImageMagik. ImageMagick n'est malheureusement pas aussi répandue que GD sur les serveurs web, si vous avez un dédié ce n'est pas un souci en revanche si vous travaillez sur un mutu c'est parfois plus délicat quoi qu'il en
soit, si les mutu ne donne pas accès a ImageMagick via php c'est bien rare que la bibliothèque n'existe pas sur le serveur et c'est pourquoi nous utiliserons la commande exec() pour y accéder.
A partir de là, plusieurs facteurs ont retenus mon attention :
1/ la qualité de l'image produite.
2/ la "vélocité du script" (donc la charge php infligée au serveur)
3/ la résultante des deux précédents donc le temps de chargement de l'image.
Voici la partie script lié purement a la fabrication de l'image :
PHP:
<span class="syntaxdefault"> </span><span class="syntaxcomment">// Calcul des nouvelles dimensions<br /></span><span class="syntaxdefault"> list</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$width</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $height</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> getimagesize</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fichier</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $facteur </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $width </span><span class="syntaxkeyword">/</span><span class="syntaxdefault"> $format</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $newwidth </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $width </span><span class="syntaxkeyword">/</span><span class="syntaxdefault"> $facteur</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $newheight </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $height </span><span class="syntaxkeyword">/</span><span class="syntaxdefault"> $facteur</span><span class="syntaxkeyword">;<br /><br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// Création de la mniature<br /></span><span class="syntaxdefault"> $cmd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"convert ${fichier} -thumbnail ${newwidth}x${newheight} -quality 50 -strip miniature.jpg"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> exec</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$cmd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $contents </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> file_get_contents</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'miniature.jpg'</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> echo $contents</span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> </span>
Je ne reviens pas sur l'extraction des données ni sur l'envoie du header, c'est pareil mais je donne quelques explications sur la commande exécutée via exec() :
convert ${fichier} -thumbnail ${newwidth}x${newheight} -quality 50 -strip miniature.jpg
Le script fait donc appel a la commande "convert" pour produire une image de taille ${newwidth}x${newheight} en qualité 50 (plus le chiffre est bas plus la qualité baisse), "strip" quand a lui vire les données exif diverses (de mémoire) afin d'alléger le poids de l'image le résultat est envoyé dans le fichier "miniature.jpg" sur le disque.
file_get_contents() lira le fichier pour ne faire un écho et comme les entêtes on été envoyé, le client recevra son image. J'utilise ici file_get_contents() car je n'ai pas trouvé le moyen d'envoyer directement sur la sortie standard de php le contenu via la ligne de commande. Nous verrons par la suite que cet inconvénient va se transformer en avantage.
Le résultat est, malgré la faible qualité demandée (afin d'économiser de la ressource), déjà bien supérieur a ce que produisais GD même référence de comparaison que précédemment :

la comparaison des deux bibliothèques en vis a vis de passe de tous commentaire :

Pour que la comparaison soit a peut prêt valable, il faut noter le poids physique de chaques images. GD fourni une image de 30,50Ko, avec les réglages choisis, ImageMagick produit une image don le poids est de 28,44Ko. A ce stade nous avons gagné sur deux points importants :
1/ la taille du fichier donc (temps de chargement, économie de Bande passante, ....)
2/ qualité picturale de l'image.
Que pouvons nous encore gagner ?
Mise en cache
Le gros souci des scripts de génération de miniature a la volée est en fait tout entier dans la ressource processeur utilisée pour la créations des images. D'une part ça mobilise php pour des taches peut enviable, ensuite ça peut vite mettre a genoux la mémoire si on brasse de grosse tailles. Que dire sinon que cette consommation se répète a chaque chargement d'une photo ...
Malgré les headers adaptés et la gestion du cache des navigateurs vous ne gagnerez pas grand chose niveau serveur si un internaute décide de visualiser beaucoup de vos photos ...
Principal inconvénient tout a l'heure, le fichier généré qu'il fallait relire pour l'envoyer vers la sortie va nous servir pour un cache intelligent ...
Un cache intelligent est pour moi un système qu'on va pouvoir manipuler pour en faire autre chose (on verra plus loin), qui est économe si possible en ressource (je part du principe que la taille des disques n'est pas un souci) et qui (normal) nous fait gagner du temps et du calcul.
Voici donc la modification du script précédent qui introduit la notion de mise en cache des miniature :
PHP:
<span class="syntaxdefault"> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">file_exists</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'cacheFile.php'</span><span class="syntaxkeyword">)){<br /></span><span class="syntaxdefault"> include</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'cacheFile.php'</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> if</span><span class="syntaxkeyword">(isset(</span><span class="syntaxdefault">$photo</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">$ident</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$format</span><span class="syntaxkeyword">])){<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// le fichier cache existe et la photo est dedans<br /></span><span class="syntaxdefault"> echo base64_decode</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$photo</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">$ident</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$format</span><span class="syntaxkeyword">]);<br /></span><span class="syntaxdefault"> exit</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}else{<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// le fichier cache existe mais la photo n'y est pas<br /></span><span class="syntaxdefault"> $cmd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"convert ${fichier} -thumbnail ${newwidth}x${newheight} -quality 50 -strip ${fichierDisque}"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> exec</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$cmd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $contents </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> file_get_contents</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fichierDisque</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> echo $contents</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// le fichier cache n'existe pas on le crée<br /></span><span class="syntaxdefault"> $fd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> fopen</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'cacheFile.php'</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> </span><span class="syntaxstring">"a"</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> <br /> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">){<br /></span><span class="syntaxdefault"> fwrite</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">,</span><span class="syntaxstring">"\t\$photo['$ident']['$format'] = \""</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">base64_encode</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$contents</span><span class="syntaxkeyword">).</span><span class="syntaxstring">"\";\n\n"</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> <br /> fclose</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}else{<br /></span><span class="syntaxdefault"> $cmd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"convert ${fichier} -thumbnail ${newwidth}x${newheight} -quality 80 -strip ${fichierDisque}"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> exec</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$cmd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $contents </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> file_get_contents</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fichierDisque</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> echo $contents</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// le fichier cache n'existe pas on le crée<br /></span><span class="syntaxdefault"> $fd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> fopen</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'cacheFile.php'</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> </span><span class="syntaxstring">"w"</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> <br /> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">){<br /></span><span class="syntaxdefault"> fwrite</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">,</span><span class="syntaxstring">"<?php \n\t\$photo['$ident']['$format'] = \""</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">base64_encode</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$contents</span><span class="syntaxkeyword">).</span><span class="syntaxstring">"\";\n\n"</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> <br /> fclose</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}</span><span class="syntaxdefault"> </span>
Le système s'articule autour d'une première condition évidente, le fichier de cache existe t il ?
1/ si non (cas else) on va générer l'image et créer e fichier de cache en la rangeant dedans
2/ si oui il faudra recourir a un second test pour savoir si l'image est dedans. En effet le cache est commun a toutes les tailles d'une photo donc pour chaque photo on aura un fichier de cache qui contiendra toutes les tailles voulues.
Le code qui gère la création du cache avec un fopen crée un fichier php (qu'on peut donc inclure très important !, mais aussi downloader, ....) qui contiens un simple ligne :
PHP:
<span class="syntaxdefault"></span><span class="syntaxkeyword"><?</span><span class="syntaxdefault">php<br /> $photo</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'dossier/photo.jpg'</span><span class="syntaxkeyword">][</span><span class="syntaxstring">'250'</span><span class="syntaxkeyword">]</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"/9j/4AAQSkZJRgABAQEBL .... WkOEITI//Z"</span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> </span>
Vous le voyez, le cache est un "script" qui assigne le contenu de l'image encodé base 64 a un variable de tableau portant le nom de l'image et sa taille en coordonnée (notez le base 64 on va y revenir :wink: ).
Dans le cas ou une photo serait demandée ailleurs avec une autre dimension une nouvelle variable de tableau serait générée pour contenir l'autre dimension dans le même fichier de cache :
PHP:
<span class="syntaxdefault"></span><span class="syntaxkeyword"><?</span><span class="syntaxdefault">php <br /> $photo</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'dossier/photo.jpg'</span><span class="syntaxkeyword">][</span><span class="syntaxstring">'250'</span><span class="syntaxkeyword">]</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"/9j/4AAQSkZJRgABAQEBL .... WkOEITI//Z"</span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> <br /> $photo</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'dossier/photo.jpg'</span><span class="syntaxkeyword">][</span><span class="syntaxstring">'900'</span><span class="syntaxkeyword">]</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"/9j/4AAQSkZJRgABAQEBL ............... WfOGITIT"</span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> </span>
Un unique fichier de cache peux donc contenir toutes les photos en différentes tailles.
Nous avions vu qu'un écho envoyait la photo demandé sur la sortie standard, là avec le dispositif de cache un include nous permet de savoir si la variable correspondant a l'image existe si oui on en fait un echo après avoir décodé son contenu ( echo base64_decode($photo[$ident][$format]) ) si non on va générer l'image, l'ajouter au fichier de cache (fopen() en mode appends) et faire un echo.
Astuce du Base 64
tant qu'on en est dans l'optimisation il faut parler de la technique qui consiste a encoder les photos directement dans le contenu de la page. Disons que ça ne fait pas gagner de volume, mais que ça peut considérablement diminuer le nombre de requêtes fait par le navigateur de l'internaute, qui au lieu de charger le code html de la page et ensuite les photos incluses va recevoir une page plus grosse encodée en html qui contiens le contenu des images.
Je vous conseille la lecture de Data uri schema pour comprendre là ou ça peut être utile et là ou ça ne l'est pas. Il est évident que ça purra aider mais pas toujours. Quoi qu'il en soit si vous devez passer une image au navigateur avec cette technique l'inclusion du fichier cache le permettra facilement puisque au final toutes les datas base 64 seront déjà dedans.
Les perfs au final
Toujours sur la base d'images "similaires" il conviens au final de se faire une idée précise des gains pour le serveur.
J'estime que pour qu'un script de miniature soit estimé correct, il dois s'approcher au plus près des performances d'une image physique présente sur le disque. Voici un comparatif :

Les portions d'images ci dessus sont issues de firefox et de son outil de débogage, je me suis concentré sur les accès réseau pour voir les différences. Le test est fait avec CRTL +F5 afin d'obliger le rechargement complet plus la résolution DNS.
On vois facilement que le temps de réception est assez similaire, normal le débit réseau pouvant être assimilé a une constante des images de poids similaire vont transiter pendant le même temps, idem pour les temps de résolution et de connexion qui sont propre a l'accès au serveur et pas au script.
Là ou ça deviens intéressant c'est qu'IMageMagick nous a fait gagner du temps de calcul (ce qui se passe durant l'attente) 71ms pour ImageMagick alors que GD prend 95ms pour le même travail.
On peut se poser la question de savoir a quoi correspond le temps d'attente pour l'image physique (41ms) je n'ai aps trouvé cette réponse du moins pas satisfaisante quoi qu'il en soit elle interviens a titre comparatif comme une valeur absolue sous laquelle il est impossible de descendre puisque a priori il n'y a aucun traitement de fait par php.
Si on en restait là, hormis le facteur qualité pictographique, les gains apportés par imageMagick ne serait pas affolants ou transcendants observons donc le même test sur une image plus importante (900px de large) :

Je ne reviens pas sur la résolution DNS, la connexion etc ... mais si on ce concentre sur l'attente on constate là un gain effectif relativement important puisque ImageMagick prend presque moitié moins de temps pour travailler (avec mise en cache et encodage base64 en plus ...) et rivalise encore bien avec l'image physique qui elle demande toujours le même temps d'attente (41 / 43 ms dans les deux cas) tout comme la version Imagemagick en cache.
Conclusions
1/ Le temps de traitement de l'image deviens donc grâce au cache une "constante".
2/ La qualité de l'image est amélioré par le choix de la bonne bibliothèque.
3/ 10 tailles d'image n'occupent qu'un unique fichier et pas 10.
4/ la gestion des images passe par un seul script.
Cerise sur le sunday
Vue les préoccupations générales quand au droits d'auteur un petit watermark s'imposait :
PHP:
<span class="syntaxdefault"> $mentionCopy </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"www.example.com"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $cmd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">" convert -size 150x14 xc:none -gravity center \<br /> -stroke black -strokewidth 2 -annotate 0 $mentionCopy \<br /> -background none -shadow 150x3+0+0 +repage \<br /> -stroke none -fill white -annotate 0 $mentionCopy \<br /> ${fichierDisque} +swap -gravity south -geometry +0-3 \<br /> -composite ${fichierDisque}"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> exec</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$cmd</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> </span>

Ici photos en 250px, 125px, 900px (extrait)
Le script
PHP:
<span class="syntaxdefault"><?php<br /> $comut </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> true</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> for</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">;</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword"><</span><span class="syntaxdefault">strlen</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'data'</span><span class="syntaxkeyword">]);</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">++){<br /></span><span class="syntaxdefault"> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$comut</span><span class="syntaxkeyword">){<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// on a pas trouvé le slash<br /></span><span class="syntaxdefault"> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'data'</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">]==</span><span class="syntaxstring">'/'</span><span class="syntaxkeyword">){<br /></span><span class="syntaxdefault"> $comut </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> false</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}else{<br /></span><span class="syntaxdefault"> $format </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $format</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'data'</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">];<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}else{<br /></span><span class="syntaxdefault"> $fichier </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $fichier</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'data'</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">];<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> $ident </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $fichier</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $fichier </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $_SERVER</span><span class="syntaxkeyword">[</span><span class="syntaxstring">'DOCUMENT_ROOT'</span><span class="syntaxkeyword">].</span><span class="syntaxstring">'/images/'</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$fichier</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $fichierDisque </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> md5</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$ident</span><span class="syntaxkeyword">).</span><span class="syntaxstring">'.jpg'</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $mentionCopy </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"www.example.com"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> <br /> </span><span class="syntaxcomment">// header php<br /></span><span class="syntaxdefault"> $offset </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> 290304000</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $expire </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> gmdate </span><span class="syntaxkeyword">(</span><span class="syntaxstring">"D, d M Y H:i:s"</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> time</span><span class="syntaxkeyword">()</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">+</span><span class="syntaxdefault"> $offset</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">.</span><span class="syntaxdefault"> </span><span class="syntaxstring">" GMT"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"Last-Modified: "</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">.</span><span class="syntaxdefault"> gmdate</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"D, d M Y H:i:s"</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">.</span><span class="syntaxdefault"> </span><span class="syntaxstring">" GMT"</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"Cache-Control: max-age=$offset, public"</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"expires: "</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">$expire</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'Content-Type: image/jpeg'</span><span class="syntaxkeyword">);<br /><br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// Calcul des nouvelles dimensions<br /></span><span class="syntaxdefault"> list</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$width</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $height</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> getimagesize</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fichier</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $facteur </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $width </span><span class="syntaxkeyword">/</span><span class="syntaxdefault"> $format</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $newwidth </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $width </span><span class="syntaxkeyword">/</span><span class="syntaxdefault"> $facteur</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> $newheight </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> $height </span><span class="syntaxkeyword">/</span><span class="syntaxdefault"> $facteur</span><span class="syntaxkeyword">;<br /><br /></span><span class="syntaxdefault"> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">file_exists</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'cacheFile.php'</span><span class="syntaxkeyword">)){<br /></span><span class="syntaxdefault"> include</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'cacheFile.php'</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> if</span><span class="syntaxkeyword">(isset(</span><span class="syntaxdefault">$photo</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">$ident</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$format</span><span class="syntaxkeyword">])){<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// le fichier cache existe et la photo est dedans<br /></span><span class="syntaxdefault"> echo base64_decode</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$photo</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">$ident</span><span class="syntaxkeyword">][</span><span class="syntaxdefault">$format</span><span class="syntaxkeyword">]);<br /></span><span class="syntaxdefault"> exit</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}else{<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// le fichier cache existe mais la photo n'y est pas<br /></span><span class="syntaxdefault"> $cmd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"convert ${fichier} -thumbnail ${newwidth}x${newheight} -quality 50 -strip ${fichierDisque}"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> exec</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$cmd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $cmd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">" convert -size 150x14 xc:none -gravity center \<br /> -stroke black -strokewidth 2 -annotate 0 $mentionCopy \<br /> -background none -shadow 150x3+0+0 +repage \<br /> -stroke none -fill white -annotate 0 $mentionCopy \<br /> ${fichierDisque} +swap -gravity south -geometry +0-3 \<br /> -composite ${fichierDisque}"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> exec</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$cmd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $contents </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> file_get_contents</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fichierDisque</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> echo $contents</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// le fichier cache n'existe pas on le crée<br /></span><span class="syntaxdefault"> $fd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> fopen</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'cacheFile.php'</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> </span><span class="syntaxstring">"a"</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> <br /> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">){<br /></span><span class="syntaxdefault"> fwrite</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">,</span><span class="syntaxstring">"\t\$photo['$ident']['$format'] = \""</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">base64_encode</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$contents</span><span class="syntaxkeyword">).</span><span class="syntaxstring">"\";\n\n"</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> <br /> fclose</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}else{<br /></span><span class="syntaxdefault"> $cmd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"convert ${fichier} -thumbnail ${newwidth}x${newheight} -quality 80 -strip ${fichierDisque}"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> exec</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$cmd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $cmd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">" convert -size 150x14 xc:none -gravity center \<br /> -stroke black -strokewidth 2 -annotate 0 $mentionCopy \<br /> -background none -shadow 150x3+0+0 +repage \<br /> -stroke none -fill white -annotate 0 $mentionCopy \<br /> ${fichierDisque} +swap -gravity south -geometry +0-3 \<br /> -composite ${fichierDisque}"</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> exec</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$cmd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> $contents </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> file_get_contents</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fichierDisque</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> echo $contents</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">// le fichier cache n'existe pas on le crée<br /></span><span class="syntaxdefault"> $fd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> fopen</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'cacheFile.php'</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> </span><span class="syntaxstring">"w"</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> <br /> if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">){<br /></span><span class="syntaxdefault"> fwrite</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">,</span><span class="syntaxstring">"<?php \n\t\$photo['$ident']['$format'] = \""</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">base64_encode</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$contents</span><span class="syntaxkeyword">).</span><span class="syntaxstring">"\";\n\n"</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> <br /> fclose</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fd</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault">?></span>
Ce script est (chez moi) dans un dossier physique "/images" les photos a gérer sont dans des sous dossier de "/images".
Il manque :
* La destruction de la miniature physique crée (après la mise en cache) $fichierDisque.
* La gestion du nom des fichiers cache ('cacheFile.php'), je vous laisse plancher la dessus c'est pas compliqué à adapter.
Si vous avez des idées d'optimisation je suis preneur.