[script] miniature et cache d'image à la volée

Discussion dans 'Développement d'un site Web ou d'une appli mobile' créé par zeb, 13 Décembre 2013.

  1. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    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 :

    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>
    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

    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 :
    [​IMG]
    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 :

    [​IMG]

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

    [​IMG]

    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 :

    [​IMG]

    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) :

    [​IMG]

    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>
    Voici le principe que j'ai adopté pour rester dans la ligne du reste du code.

    [​IMG]
    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.
     
  2. UsagiYojimbo
    UsagiYojimbo WRInaute accro
    Inscrit:
    23 Novembre 2005
    Messages:
    11 875
    J'aime reçus:
    72
    ImageMagick est de toute manière une pure tuerie, bien trop sous-estimé à mon goût (et en effet capable de générer des images de qualité autrement supérieure à celle de GD, notamment dans des cas d'images avec transparence). Et en effet, niveau traitement, ça bombarde.
     
  3. freestyle83
    freestyle83 WRInaute occasionnel
    Inscrit:
    19 Novembre 2011
    Messages:
    356
    J'aime reçus:
    9
    Ouahou ! très bien écrit très clair ! Merci à toi Zeb pour ce partage en or !
     
  4. forty
    forty WRInaute passionné
    Inscrit:
    30 Octobre 2008
    Messages:
    1 929
    J'aime reçus:
    0
    Moi j'utilise la fonction imagecopyresampled qui, comme l'indique l'aide, rééchantillonnée de manière à conserver la clarté de l'image durant une réduction.
     
  5. chtipepere
    chtipepere WRInaute occasionnel
    Inscrit:
    8 Janvier 2004
    Messages:
    449
    J'aime reçus:
    0
    Très intéressant.

    Pour optimiser encore un peu tes images, je te propose d'utiliser en plus l'outil de Yahoo, SmushIt, une fois ta première optimisation effectuée.
    Une classe php est disponible : http://www.pitgroup.nl/demo/smushit/SmushIt.txt

    Cet outil va optimiser l'image, sans perte, en utilisant différentes techniques :
    - jpegtran
    - suppression des meta-données inutiles
    - plus d'informations ici (http://developer.yahoo.com/yslow/smushit/faq.html)

    Personnellement, je ne suis pas pour l'utilisation d'un fichier php pour afficher l'image cachée.
    Les performances seront bien meilleures en générant directement la vraie image sur le disque (les calls suivant seront traités par apache). Pour ce faire, il suffit de générer l'image en respectant l'arborescence demandée et en corrigeant les règles de ré-écriture :
    Code:
       # traitement des miniatures
       RewriteCond %{REQUEST_URI}  ^(.*)thumb.*
       RewriteCond %{REQUEST_FILENAME} !-f
       RewriteRule ^(.*)thumb(.*)$ /images/images.php?data=$2 [L]
    My two cents
     
  6. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    @ chtipepere

    les métas données sont déjà virées :
    http://www.imagemagick.org/Usage/resize/
    à propos de Jpegtran :
    http://desgeeksetdeslettres.com/programmation-java/jpegtran-un-outil-p ... mages-jpeg
    le point 1 est inutile, le 2 est déjà géré (cf ci dessus), le 3 ne se justifie pas forcement, le 4 est géré aussi, le 5 c'est commun.
    humm, comment dire ?! As tu regardé le code dont tu parle ? il fait appel a des webservices externes pour peaufiner ton image c'est tout sauf du gain de performance ... Sans parler du fait que tu est dépendant d'un serveur externe qui peut fermer.

    beaucoup d'hébergements mutu (c'est spécifié que c'est une contrainte prise en compte) bloquent la quantité de fichier possibles a la création. (ex : 260 000 chez 1&1), de plus la multiplications des fichiers dans un dossier n'est pas idéale. Pour finir (la cerise) une contrainte est de pouvoir manipuler l'image dans une simple variable.
    Php permet ça : 12 tailles ou une -> un fichier temps de traitement constant.

    Sinon si il n'avait été que de gérer de miniatures, cela n'aurait pas été un souci en effet
     
  7. Blount
    Blount WRInaute impliqué
    Inscrit:
    18 Novembre 2010
    Messages:
    707
    J'aime reçus:
    0
    Le problème de ton cache, c'est que plus il y aura d'image plus ton fichier consommera en mémoire pour inclure ton fichier cache. À long terme, sur un mutu, ça risque d'atteindre la limite mémoire.
    Personnellement, j'aurai aussi opté pour la solution que t'a proposé chtipepere. Rien ne t'empêche d'utiliser plusieurs répertoires (un par largeur par exemple).

    Pour ma part, je passe par Imagick qui est une extension PHP permettant d'exploiter ImageMagick directement via une classe PHP. Plus besoin de "exec".

    Attention aussi au data URI, il y a des limites aux niveau des navigateurs (notemment IE).
     
  8. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    Non c'est précisé dans l'article (besoin de 4 tailles d'image) ... un fichier cache par image.
    J'y ai pensé ça va de sois. le fichier créé n'est pas ingérable loin s'en faut puisque on reste dans des tailles de data compatible avec une page web (poids d'image restreint) qu'il sert surtout a mémoriser des miniatures (donc des images par définition moins lourdes que l'original)

    En chiffre le fichier cache pour les tailles utilisées (250, 900, 160)+ original fait un poil plus que l'image originale 1024xX. Cela reviens a dire que tu gère un poil plus que deux fois le poids de ton image (volume disque) hors le volume mémoire d'une image étant dans les limites acceptables (mémoire) ça passe.

    Pas dispo partout en mutu d’où le exec() comme précisé.

    Bon après c'est pas le script révolution hein :wink: ... on parle d'offrir un système réfléchi et optimisé dans des conditions précises comme un mutu avec des ressources limitées et standards, pas d'un dédié où on peut faire toutes les fantaisies en couplant en plus un CDN pour gagner encore en optimisation client ... D'ailleurs si on partait la dessus je pense qu'il serait même bon de parler de sous-domaine cookies-less avec un bon script CGI en C pour produire les images a la volée en toute efficacité et éviter ainsi pas mal de couches logicielles liées a php et au système.
     
  9. Blount
    Blount WRInaute impliqué
    Inscrit:
    18 Novembre 2010
    Messages:
    707
    J'aime reçus:
    0
    J'ai quand même du mal à comprendre, à partir de où tu utilise un fichier PHP par image ?
    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"> </span>
    C'est le même fichier non ? J'ai du louper un truc :)

    Concernant la classe Imagick non dispo sur les mutus, je ne penses pas que la commande convert via exec soit mieux loti, si ?
    Ce serait d'ailleurs, pour moi, le contraire.

    Justement, chaque mutu a sa propre config et demande donc une adaptation (rien que le convert en ligne de commande).
    Après, c'est très bien d'avoir ouvert ce sujet, ça profitera à beaucoup de monde je pense.
     
  10. UsagiYojimbo
    UsagiYojimbo WRInaute accro
    Inscrit:
    23 Novembre 2005
    Messages:
    11 875
    J'aime reçus:
    72
    Quelqu'un a testé les différences de temps d'exécution entre convert en ligne de commande et Imagick ? Sachant que lorsque j'avais testé Imagick, il y a quelques années, ça m'avait surtout l'air d'être bridé vis à vis des possibilités de convert en ligne de commandes. Il y a certains films qui ne fonctionnaient pas ce me semble...
     
  11. FortTrafic
    FortTrafic WRInaute passionné
    Inscrit:
    11 Décembre 2012
    Messages:
    1 210
    J'aime reçus:
    18
    Faut faire défiler dans la balise CODE (l'ascenseur n'est pas visible avant de faire défiler, les joies de la modernité), dans le "else" de ce "if(file_exists('cacheFile.php')){" il y a la partie ou il créé un fichier cacheFile.php pour y stocker les données de l'image (encodées base 64) dans un tableau, en plusieurs tailles.
     
  12. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    C'est le moins qu'on puisse dire :D (fin du post)
    Oui je pense que tu as compris l'idée je suis pas là pour fournir du script pré-mâché et immédiatement dispo pour tout le monde faut se creuser un peut parfois pour adapter mais bien pour introduire des notions :

    1/ ImageMagik vs GD niveau qualité
    2/ comparaison des solution dynamiques et statiques.
    3/ le cache dans l'option full dynamique
    4/ les temps réseaux liés aux images.
    5/ les options base 64 et leur limites (liens vers alsacréation sur le Data URI shéma)
    etc ...

    Ce que toi tu as fait d'ailleurs car tu a regardé le code pour en comprendre les limites (oui l'exemple ne gère pas les fichiers multiples :wink: d’où ma remarque en fin de sujet)

    Chez 1&1 je ne l'ai que comme ça impossible autrement.
    J'aurais (plus tard) l'occasion de voir ça chez OVH mais là c'est pas possible pour le moment.
    Quoi qu'il en soit c'est présent de base dans pas mal de distrib donc je pense que ça dois se trouver "facilement" en tous cas plus facilement que la lib php qui va bien. (je ne l'ai même pas en local chez moi et pourtant j'ai un paquet de conneries installées :D )

    Oui c'est l'idée, on part d'un code assez "frustre" (il y a plein de truc a y ajouter comme la gestions de referer pour le hotlinking :wink: ), on place le principe de travail (htaccess) et ensuite les gens vont délirer a leur guise, de toute façon je ne donne jamais rien (niveau code) sans qu'on soit obligé de s'impliquer un peu pour que ce soit finalisé.
     
  13. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    Blount fait allusion au fait que le nom du fichier de cache ( cacheFile.php ) est codé en dur dans le script donc qu'il ne peut y avoir qu'un unique fichier de cache, d’où sa remarque pertinente sur la possible saturation mémoire en cas d'include d'un fichier qui comprendrait des centaines de photos (les include sont mappés en mémoire donc si tu include() un ficher d'une dizaine de gigas tu met la machine a terre en un tour de code :D )
     
  14. Leonick
    Leonick WRInaute accro
    Inscrit:
    8 Août 2004
    Messages:
    19 417
    J'aime reçus:
    0
    pour récupérer le nom de ton fichier, si tu ne veux pas utiliser de regex, je pense qu'un explode("/",$_GET['data']) serait plus adapté que ton parcours de la variable
    sinon, le
    Code:
    $format = $format.$_GET['data'][$i];
    peut se simplifier en
    Code:
    $format .= $_GET['data'][$i];
     
  15. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    Merci Leonick, c'est vrai que je n'ai pas pensé a concaténer directement avec l'operateur '.=' (je traine cette manie d'un vieux manuel d’apprentissage du Basic de 1983 :oops: "let A = A + B")

    pour l'explode je me suis posé la question car dans l'exemple donné c'est évident que c'est plus cool. Mon souci est double avec explode :
    1/ le chemin ici est court > "dossier/image.jpg" mais parfois j'ai des "dossier/sous-dossier/.../image.jpg" donc explode m'aurait forcé a itérer pour avoir la partie dossier complet.
    2/ je ne sais pas comment est codé explode() en interne. J'ai zieuté la doc mais c'est vide et je me suis posé la question de savoir comment je ferais si je devais écrire la fonction. Il m'est alors venu deux options : soit un genre de pregmatch_all avec une exp reg soit une itérative qui parserait la variable pour monter le tableau de sortie. Alors je me suis dit que si les concepteurs php avait opté pour une exp régulière je n'économisait rien ; si ils avaient opté pour une itérative je ne gagnais rien mais je ne perdais rien a faire pareil. Faudrait que je me tourne vers les biblio C pour voir comment c'est foutu car ça dois venir de là.

    Merci pour ton retour ;-)
     
  16. fobec
    fobec WRInaute discret
    Inscrit:
    10 Mai 2005
    Messages:
    189
    J'aime reçus:
    0
    merci pour ton post, le retour sur la manière dont tu as abordé la problématique est instructif.

    si j'ai bien compris, les images sont mises en cache sous forme de fichier, 20 000 x 5 grosso modo 100 000 fichiers à maintenir.
    En parcourant la méthodologie, il me vient quelques remarques:
    - sprite css:
    Est-ce que la technique des sprites est envisageable ? générer qu'une seule image qui contient les 5 dimensions puis à l'affichage sélectionner la zone en css

    - SqLite:
    As-tu fait des tests sur le stockage des images dans une ou plusieurs tables SQLite ? ce serait plus simple de maintenir 10 bases que 100 000 fichiers a ranger sur le disque.
     
  17. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    Oui les images sont mises en cache de façon groupée CAD que pour une image donnée est associé un fichier de cache qui comprend les définitions de toutes les tailles utiles au site (chez moi ; là le code ne traitre pas ce souci souligné plus haut par Blount).

    sprite css

    La problématique des sprites est intéressante, seulement elle a des limites. Pour résumer (pour ceux qui ne connaissent pas) un sprite est une image globale qui en comprend plusieurs, CSS permet de positionner la vue sur la partie de cette image qui intéresse l'affichage.

    Dans le cas précis il faut comprendre que si on considère 4 tailles d'image il va falloir disposer sur une surface globale les 4 images et sachant que le rapport hauteur / largeur est constant on va obligatoirement avoir des blancs sur l'image globale. La résultante produite sera donc "énorme" en poids comparée au poids totalisés des images individuelles.
    Les sprites sont adaptés a la gestion d'images de petites tailles ou constantes.

    [​IMG]
    Là c'est un exemple de base avec des ratios entiers 100%, 200, 300, 400% donc il y a moyen de les ordonner mieux pour gagner de la surface (avoir moins de blanc) mais ça ne correspond pas a ma réalité et comment algorithmiquement parlant ordonner ça de façon auto sans savoir a l'avance les tailles qui vont être utilisées ?
    De plus si l'internaute bidouille une url (moi je le fait souvent) il faut recomposer tout le sprite ... bonjour les calculs.

    Une alternative plus pertinente pourrait être de former des sprites par groupes de tailles. Mais là on entre dans une autre problématique qui est celle du choix des regroupement ...

    [​IMG]

    Dans ce cas précis si je n'ai besoin que de l'image verte (sur ce sprite) plus une image bleu (sur un autre), bien que le regroupement des photos par taille ne me fait pas perdre de place et minimise le nombre de fichiers sur le serveur c'est l'internaute qui va souffrir du résultat.

    donc pour finir sur les sprites il reste le souci du poids, même bien géré cela implique de charger 10 /20 images pour parfois n'en utiliser qu'une.
    Après sous l'angle SEO quid des images dans les moteurs si ce sont des sprites ? ...

    SqLite

    Non j'avoue que ce soit sous SqLite ou Mysql (blobs en général) je n'ai pas fait de test simplement car c'est ajouter la connexion script / base SQL dans l'équation. Donc je ne peux donner d'idée la dessus sauf sur le fait que je doute que cela donne une perf plus intéressante. Il faut dire aussi qu'en l'état mes photos a gérer c'est 13800 fichiers (ça encore c'est pas énorme) mais cela représente déjà 2 Go de data ce qui pour une base de données est déjà un bon chiffre.

    Merci Fobec pour tes idées ;-)
     
  18. Leonick
    Leonick WRInaute accro
    Inscrit:
    8 Août 2004
    Messages:
    19 417
    J'aime reçus:
    0
    non, car tu obtiendrais un tableau. Ensuite, tu fais un implode avec tous sauf celui de la taille.
    Mais, en fait, si tu n'a que 2 parties, le mieux étant de faire un substr avec un strpos pour trouver la position du 1° "/"
    ça fait pas beaucoup pour seulement 13800 images (même avec les différentes tailles) ?
     
  19. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    Non ça c'est juste les "originaux" redimensionnés à 1024 px max (hauteur ou largeur) et compressés a 75%.
    Si je garde les photos uploadées par les internautes c'est monstrueux (les photos viennent quasi toutes d'APN)
     
  20. Koxin-L.fr
    Koxin-L.fr WRInaute passionné
    Inscrit:
    15 Janvier 2012
    Messages:
    1 891
    J'aime reçus:
    8
    Alors, ca c'est un top tuto... dont je vais m'inspirer pour de futur projet.

    Par contre y a juste un truc sur lequel je voudrais revenir.

    Tu prétend préférer travailler le redimensionnement à la volée plutôt qu'à l'enregistrement de l'image pour éviter d'avoir x images identiques de tailles différentes en stock.

    Sauf que techniquement parlant, il est plus rapide d'appeler une image déjà à la bonne taille et que financièrement parlant la performance serveur est bien plus couteuse que l'espace disk.

    Je pense donc, que l’intérêt réside surtout dans le gain de temps sur une éventuelle décision ultérieur de changer les dimension des impages ainsi créée. Pas la peine de tout reloader à la bonne taille.
     
  21. Leonick
    Leonick WRInaute accro
    Inscrit:
    8 Août 2004
    Messages:
    19 417
    J'aime reçus:
    0
    surtout qu'en tant qu'internaute, j'ai plus de patience sur un upload d'image que pour uniquement un affichage d'images.
     
  22. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    Oui en effet, tu as raison, il y a un choix économique qui peut invalider un choix technologique par rapport a un autre.

    Oui c'est clair c'est pour cette raison que je met dans la partie "Les perfs au final" une comparaison ImageMagick, GD et fichier afin qu'on se rend compte du truc.

    Après si on creuse la solution sous l'angle de ses limites on arrive a des constats liés au serveur :

    1/ mutu avec pas trop d'images > tout mettre en dur est surement la solution, le script peut aider (certaines portions) a générer les miniatures. Il trouvera une place en back office par exemple.

    2/ dédié avec beaucoup ou peu de photos > ce ne sera pas le critère place ou nombre de fichier qui coincera, là encore on peut envisager le stockage en dur, on sera gagnant sur les temps d'accès et la ressource CPU. Là aussi on peut se servir du script uniquement pour générer les images inutiles de les mettre en cache. Au passage sous domaine cookies-less, voir un petit serveur light (pas besoin d'apache) et c'est Byzance.

    3/ mutu avec pas mal d'images (c'est mon cas) > là je suis bloqué par les limite du contrat. le système occupe environ 160 000 fichiers (CMS multisite avec cache d'output), bref j'ai 100 000 fichiers dispo encore devant moi et pas plus. A ce stade je sais par exp qu'il faut y aller doucement dans la création donc si je balance 4 x 20 000 photos là je me rapproche dangereusement de la limite donc c'est pas possible. La génération dynamique pur des miniature comble le problème mais le serveur rame, il faut le cache qui lui ne peut être "simple" (ce qui reviendrais à stocker chaque image) il se doit d'en garder plusieurs donc j'ai opté pour les variations de taille.

    Après financièrement parlant, passer le cap dédié est délicat car il existe un gouffre technique entre le mutu plein pot (niveau prix et perf) et le dédié juste un poil plus cher (qui est généralement moins musclé que la config mutu). Bref quand on se trouve dans la tranche intermédiaire (ce qui a motivé ce travail chez moi) il faut que le site justifie pleinement l'offre dédié midle game pour qu'on privilégie l'espace disque a gogo avec la multiplication des fichiers au mutu bien rodé qui ronronne sans se poser de questions.
    Mais dans le principe j'abonde avec ceux qui parlent de stocker les images en dur, il ne peut y avoir plus rapide les graphes le montrent.

    En effet il y a aussi cet avantage. Tu change par exemple le design du site et ta surface "plein contenu" gagne 100px, tu peux avec un regex donner un coup sur toutes les url d'images du contenu et tu te retrouve avec des images pleine largeur sans sourciller ... idem aussi quand tu travaille sur de la techno mobile qui est gérée avec des template différente, tu charge juste ce qu'il faut ... et pas plus.

    Bref comme déjà dit c'est pas la solution miracle c'est une approche de l'image en multi taille dans un contexte précis. On y trouvera en tous cas un comparatif techno (GD vs ImagMagick) et des soluces a divers points de détail comme les appels systèmes a une bibliothèque pas dispo en php etc ...

    Note : Beaucoup de systèmes qui testent l'optimisation des sites parlent de servir des "scaled images" (de mémoire pas faire attention a l'ortho). Bref quand on en est a indiquer les height et width dans les balises images il faut en plus servir des média de cette taille et pas d'un autre. D'un point de vu utilisateur ça changera rien de charger une image 800 600 là ou on l'affiche en 400 300 mais disons que par principe autant aller jusqu'au bout et être en mesure de lâcher vraiment la bonne image. Là le fait de paramétrer la largeur de l'image dans l'url sans savoir sa largeur réelle est un plus incontestable.
     
  23. Blount
    Blount WRInaute impliqué
    Inscrit:
    18 Novembre 2010
    Messages:
    707
    J'aime reçus:
    0
    Coté amélioration du code, comme je n'aime pas trop la redondance de code, je te proposerais bien de remplacer ceci:
    PHP:
    <span class="syntaxdefault"><br />        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">}<br /></span><span class="syntaxdefault"> </span>
    Par:
    PHP:
    <span class="syntaxdefault"><br /></span><span class="syntaxkeyword">if(</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">}<br />}<br /></span><span class="syntaxdefault">$cmd </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"convert "</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">escapeshellarg</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$fichier</span><span class="syntaxkeyword">).</span><span class="syntaxstring">" -thumbnail ${newwidth}x${newheight} -quality 80 -strip "</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">escapeshellarg</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$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="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 /></span><span class="syntaxkeyword">if(</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 />}<br /></span><span class="syntaxdefault"> </span>
    Comme ton "exit" stop le script, il n'ira pas plus loin. Tu remarqueras aussi l'utilisation de escapeshellarg qui permet de sécuriser les arguments pour la commande shell.
    C'est un peu comme les injections SQL. Un fichier pourrait avoir un nom permettant de faire des chose louches. Exemple:
    ; rm -rf .; convert truc.png
    Ça donnerait la commande suivante:
    convert ; rm -rf .; convert truc.png -thumbnail
    Le point virgule permet d'exécuter plusieurs commandes l'une derrière l'autre. La différence avec && comme séparateur, c'est que même si la première commande échoue, les commandes suivantes sont exécutées.
    Ce serait ballot quand même ^^
    Bon là, c'est extrême. Mais un simple espace dans le nom du fichier casserait la commande puisque ce serait considéré comme 2 arguments.


    Pour récupérer le format et le chemin du fichier, tu peux utiliser substr comme l'a souligné Leonick:
    PHP:
    <span class="syntaxdefault"><br />$data </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> ltrim</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"> </span><span class="syntaxstring">"/"</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault">if </span><span class="syntaxkeyword">(</span><span class="syntaxdefault">false </span><span class="syntaxkeyword">===</span><span class="syntaxdefault"> $premierSlash </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> strpos</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$data</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> </span><span class="syntaxstring">"/"</span><span class="syntaxkeyword">))</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">{<br /></span><span class="syntaxdefault">    </span><span class="syntaxcomment">// mauvais format<br /></span><span class="syntaxdefault">    header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"HTTP/1.0 404 Not Found"</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault">    exit</span><span class="syntaxkeyword">;<br />}<br /></span><span class="syntaxdefault">$format </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> substr</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$data</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 0</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $premierSlash</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault">$fichier </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> substr</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$data</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $premierSlash</span><span class="syntaxkeyword">+</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault"> </span>
    Ce qui donne avec $_GET['data'] = "/250/dossier/photo.jpg";:
    Code:
    string(3) "250"
    string(17) "dossier/photo.jpg"
    
     
  24. Madrileño
    Madrileño Membre Honoré
    Inscrit:
    7 Juillet 2004
    Messages:
    32 136
    J'aime reçus:
    273
    Merci Zeb pour ce topic, une reco. :)
     
  25. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    Merci Madri ;-)

    @Blount une reco de ma part et un merci pour ton retour c'est très pertinent (tout autant que les remarque de Leonick sur l'extraction des datas). Dans les faits, le nom de fichier ne peut être manipulé (c'est un hash md5 lié a la date de partage chez moi) mais dans le principe protéger cette éventuelle entrée "utilisateur" est plus que necessaire, bravo d'y avoir pensé j'avoue que j'étais a l'ouest de ce côté. Tout utilisation différente du code pourrait ouvrir la porte a des débordements sauvages et potentiellement mortels pour le site. :oops:

    Pas con aussi la remarque sur l'exit il est vrai que c'est plus lisible et concis sur le coup :wink:

    Merci encore.
     
  26. Blount
    Blount WRInaute impliqué
    Inscrit:
    18 Novembre 2010
    Messages:
    707
    J'aime reçus:
    0
    J'ai autre chose à te proposer.

    Tu indiques ne pas trop aimer les RegExp à cause des ressources que ça "bouffe".
    Personnellement, je préfère une bonne RegExp bien exploitée qu'un code "bricole" pour le remplacer.

    Ce qui est intéressant, c'est que tu ne veux pas utiliser de RegExp dans ton PHP alors que tu l'utilises dans la configuration de ta redirection Apache, et pas très bien en plus.
    Si je reprend ton code:
    Code:
    # traitement des miniatures
    RewriteCond %{REQUEST_URI}  ^(.*)thumb.*
    RewriteRule ^(.*)thumb(.*)$ /images/images.php?data=$2 [L]
    
    Dans ces deux lignes, tu fais deux fois la même RegExp. Une fois dans RewriteCond et une fois dans RewriteRule (pourquoi RewriteCond d'ailleurs ?).
    En plus de cela, tu utilises des parenthèses capturantes alors que celle-ci sont un peu plus consommatrice en ressource.

    Je te propose donc une solution pour améliorer ta redirection ainsi que le code PHP:
    Code:
    # traitement des miniatures
    RewriteRule .*/thumb/([0-9]+)/(.*) /images/images.php?format=$1&fichier=$2 [L]
    
    Ensuite, à la place du code pour parser l'url en PHP, tu as juste ceci à faire:
    PHP:
    <span class="syntaxdefault"><br />$format </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> isset</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">"format"</span><span class="syntaxkeyword">])?</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">"format"</span><span class="syntaxkeyword">]:</span><span class="syntaxdefault">0</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault">$fichier </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> isset</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">"fichier"</span><span class="syntaxkeyword">])?</span><span class="syntaxdefault">$_GET</span><span class="syntaxkeyword">[</span><span class="syntaxstring">"fichier"</span><span class="syntaxkeyword">]:</span><span class="syntaxstring">""</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault"> </span>

    Ensuite, il y a un problème non pris en charge ici. Que se passe t-il si je crée un script comme ceci:
    PHP:
    <span class="syntaxdefault"></span><span class="syntaxkeyword"><?</span><span class="syntaxdefault">php<br />$i </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> 100</span><span class="syntaxkeyword">;<br /></span><span class="syntaxdefault">while </span><span class="syntaxkeyword">(</span><span class="syntaxdefault">true</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">{<br /></span><span class="syntaxdefault">    file_get_contents</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"http://example.com/"</span><span class="syntaxkeyword">.(</span><span class="syntaxdefault">$i</span><span class="syntaxkeyword">++).</span><span class="syntaxstring">"/dossier/photo.jpg"</span><span class="syntaxkeyword">);<br />}<br /></span><span class="syntaxdefault"> </span>
    Et que je l’exécute ?
    C'est débile, mais une fois que tu publies ton code, tu met en danger les sites l'exploitant, dont les tiens :)

    Il serait donc bien de définir une liste de formats autorisés. Par exemple:
    PHP:
    <span class="syntaxdefault"><br />$formats </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> array</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">250</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 300</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 400</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> </span><span class="syntaxcomment">// etc.<br /></span><span class="syntaxdefault">if </span><span class="syntaxkeyword">(!</span><span class="syntaxdefault">in_array</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$format</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> $formats</span><span class="syntaxkeyword">))</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">{<br /></span><span class="syntaxdefault">    header</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"HTTP/1.0 404 Not Found"</span><span class="syntaxkeyword">);<br /></span><span class="syntaxdefault">    exit</span><span class="syntaxkeyword">;<br />}<br /></span><span class="syntaxdefault"> </span>
    ou directement au niveau de la redirection:
    Code:
    # traitement des miniatures
    RewriteRule .*/thumb/(250|300|400)/(.*) /images/images.php?format=$1&fichier=$2 [L]
    
    Les autres requêtes passeront en 404.

    Si d'autres trucs me viennent en tête, je te fais signe :D
     
  27. zeb
    zeb WRInaute accro
    Inscrit:
    5 Décembre 2004
    Messages:
    12 196
    J'aime reçus:
    1
    Bien vu Blount je regarde ça dans les heures qui suivent (surement demains matin a tête reposée) ça risque même de me faire gagner de la ressource ailleurs ;-) merci.
     
  28. anemone-clown
    anemone-clown WRInaute passionné
    Inscrit:
    11 Novembre 2007
    Messages:
    1 590
    J'aime reçus:
    19
    Je sais que le sujet a plus d'un an, mais avec la possibilité d'indiquer plusieurs tailles d'images en fonction des contextes d'écrans en html5, via l'attribut "srcset" de la balise "img", le stockage physique des images dans diverses tailles est une option qui répond aux capacité du Responsive Design sans modifier l'adresse de l'image "à indexer" par les moteurs de recherche : http://www.w3.org/html/wg/drafts/html/master/semantics.html#attr-img-srcset et https://html.spec.whatwg.org/multipage/embedded-content.html#device-pixel-ratio
     
Chargement...
Similar Threads - [script] miniature cache Forum Date
[script] anti spam referrer Développement d'un site Web ou d'une appli mobile 20 Août 2015
[script] changer un texte en images Développement d'un site Web ou d'une appli mobile 10 Décembre 2013
[Script] Ajouter les boutons de partage des réseaux sociaux sans le tracking Développement d'un site Web ou d'une appli mobile 25 Août 2013
[Script] Clone légal? Développement d'un site Web ou d'une appli mobile 7 Avril 2013
[script] Comment charger un script différent suivant l'heure ? Help ! Développement d'un site Web ou d'une appli mobile 4 Octobre 2012
[script] Oméga-Annonces 1.5.1 - Php/MySqL Développement d'un site Web ou d'une appli mobile 4 Septembre 2012
[script] tracking formulaire d'affiliation Développement d'un site Web ou d'une appli mobile 5 Août 2012
[Script] - Interroger et manipuler Wordpress directement via un script PHP en crontab Développement d'un site Web ou d'une appli mobile 26 Avril 2012
[Script] Pour charger un fichier et le mettre sur son serveur Développement d'un site Web ou d'une appli mobile 6 Mars 2012
[SCRIPT] Topsite Développement d'un site Web ou d'une appli mobile 28 Août 2011
  1. Ce site utilise des cookies. En continuant à utiliser ce site, vous acceptez l'utilisation des cookies.
    Rejeter la notice