Lecture : 8 min. Auteur(s) : Tags :

La performance de vos sites et applications est devenue un enjeux majeur dans le développement Web. Servir des images optimisées et redimensionnées à vos utilisateurs est une bonne pratique de performance qui va bénéficier à la fois à votre référencement et à votre expérience utilisateur.

Dans le cadre d’une mission chez un de nos clients, nous avons dû mettre en place une solution de redimensionnement d’images dynamique. Cette solution ne devait pas venir remplacer le référentiel (un gros espace de stockage) et le workflow de publication des images. Elle devait venir s’interfacer entre l’internaute et le dépôt central (référentiel).

Nous allons vous détailler dans cet article la solution que nous avons retenue et comment nous l’avons intégrée à une API Symfony.

Contexte client : « Performance matters »

Si votre image source fait 15Mo (j’exagère) et que vous avez juste besoin d’afficher une vignette de 200×200, c’est une mauvaise pratique de fournir à l’utilisateur l’image originale et redimensionner l’image en CSS.

Tous vos utilisateurs ne disposent pas :

de la fibre ;

d’un super forfait 4G. À l’étranger, ils sont peut-être limités à 500 Mo ;

du PC dernier cri qui va réussir à redimensionner l’image en un instant.

Pour un même type de contenu et selon le vecteur de diffusion (mobile, tablette, desktop, …), vous n’allez pas vouloir fournir à l’utilisateur la même taille d’image.

Nous maintenons une API de mise à disposition de métadonnées. Cette API permet de fournir les données à l’ensemble des vecteurs de diffusion (mobile, tablette, desktop). Nous possédons un workflow de validation des images assez complexe. Une image validée est uploadée dans un espace de stockage disponible en ligne (référentiel des images) et la référence de cette image (une URL privée) est retournée dans les métadonnées fournies par l’API.

Nous ne souhaitions pas modifier le workflow de validation ou le référentiel des images, mais plutôt pouvoir mettre à disposition des clients de l’API des images optimisées, dont la taille pourrait varier selon le vecteur de diffusion.

Il était donc nécessaire de mettre en place une solution d’optimisation des images qui se positionnerait entre le référentiel des images et l’internaute, et dont l’intégration avec l’API existante serait assez simple.

Pour des raisons de coûts et pour éviter les contraintes liées à la passation de commandes (marchés publics, …), notre choix s’est vite arrêté sur une solution Open Source Thumbor.

Thumbor ? Késako ?

Thumbor est une solution open-source écrite en Python s’appuyant sur les frameworks Tornado et OpenCV. Il propose des mécanismes de redimensionnement des images mais également des filtres permettant la transformation des images (flou, contraste, …). L’une des fonctionnalités la plus intéressante est le smart-cropping : la possibilité de redimensionner l’image en se basant sur son centre d’intérêt (voir plus bas).

Thumbor s’interface facilement à votre existant à partir du moment où votre image est accessible via une URL. Il va s’occuper de récupérer l’image source, de la stocker localement (cache) et de générer les images dans les dimensions que vous lui demanderez. Les nouvelles images générées seront conservées en cache. Les images originales continuent d’être hébergées à leur emplacement original.

Installer rapidement un Thumbor fonctionnel

Comme souvent, si vous avez besoin de tester rapidement un outil, Docker est votre ami. Cette image Docker pour Thumbor est assez complète.

Pour tester les exemples à venir, vous pouvez utiliser la configuration suivante :

$ docker run -p 8000:8000 \ -e DETECTORS="[ \ 'thumbor.detectors.glasses_detector', \ 'thumbor.detectors.face_detector', \ 'thumbor.detectors.feature_detector', \ 'thumbor.detectors.profile_detector' \ ]" apsl/thumbor

Thumbor sera ensuite accessible à l’adresse localhost:8000.

Récupération des images depuis une source externe

Si vous souhaitez redimensionner cette superbe image de profil :

en la redimensionnant en 300×200, l’URL d’appel “Thumbor” sera de ce type :

http://localhost:8000/unsafe/300x200/jolicode.com/media/cache/team_cover/media/original/team/francois-dume-cover.jpg

Le résultat sera le suivant :

Par défaut, Thumbor est capable de récupérer des photos depuis n’importe où sur internet. Pour des raisons évidentes de sécurité (éviter qu’un tiers utilise votre infrastructure pour redimensionner des images), il est possible de mettre en place différents mécanismes.

Par exemple, il est possible de définir une liste blanche de domaines autorisés :

ALLOWED_SOURCES = ["https://jolicode.com"]

Cette configuration indiquera à Thumbor de n’accepter que les images provenant du domaine jolicode.com.

Il est également possible de signer chaque URL par un token qui dépendra des paramètres et de l’URL de l’image source (voir la documentation) :

## The security key thumbor uses to sign image URLs ## Defaults to: MY_SECURE_KEY SECURITY_KEY = 'MY_SECURE_KEY'

À la place de « unsafe » dans l’URL, vous devrez ajouter le hash de l’image et des ses paramètres.

Exemple :

http://localhost:8000/1234567890123456789012345678/300x200/smart/path/to/image.jpg

Smart cropping

L’une des fonctionnalités les plus intéressantes de Thumbor est le smart cropping. Thumbor va vous proposer un mécanisme qui va vous permettre de redimensionner des images en fonction du point d’intérêt de l’image.

Prenons cette image de profil de Grégoire :

Si nous appliquons un redimensionnement de 300×200 comme précédemment :

http://localhost:8000/unsafe/300x200/jolicode.com/media/cache/team_cover/media/original/team/gregoire-header.jpg)

l’image que Thumbor va générer ressemble à :

Pas génial…

En appliquant un filtre de smart cropping, Thumbor va détecter le centre d’intérêt de l’image et appliquer le redimensionnement en fonction de celui ci (ici, Grégoire).

http://localhost:8000/unsafe/300x200/smart/jolicode.com/media/cache/team_cover/media/original/team/gregoire-header.jpg

C’est nettement mieux.

Bien entendu, le smart cropping ne fonctionnera pas à tous les coups mais cela réglera la plupart des cas.

Thumbor propose plein d’autres filtres. Nous vous invitons à consulter la documentation pour en apprendre davantage.

Intégration dans un projet Symfony

Nous venons de voir une présentation succincte de Thumbor. Voici maintenant comment nous avons intégré Thumbor dans notre API.

Afin de simplifier l’usage de Thumbor et de centraliser sa configuration, nous avons intégré la génération des URLs Thumbor dans l’API principale.

Pour ce faire, nous utilisons la librairie 99designs/phumbor. Cette librairie a l’avantage d’avoir mis en place toute la complexité liée à la génération des URLs sécurisées (la génération du hash en fonction des paramètres, notamment).

Exemple pour générer une URL avec le smart detection dans une taille width/height avec suppression du profil colorimétrique :

$builderUrl = $this->thumborFactory ->url($url) ->smartCrop(true) ->resize($width, $height) ->addFilter('strip_icc');

Dans notre cas, nous avons intégré la génération des URLs lors de la sérialisation. Nous stockons dans la base de données l’URL privée et lors de la sérialisation d’un objet image contenant une URL privée nous générons une URL Thumbor.

Afin de ne pas exposer le domaine où sont stockées les images privées, nous faisons un petit remplacement avant l’envoi à l’utilisateur.

Cette URL

http://localhost:8000/unsafe/300x200/smart/jolicode.com/media/cache/team_cover/media/original/team/gregoire-header.jpg

va être transformée en

http://localhost:8000/unsafe/300x200/smart/jolicode/media/cache/team_cover/media/original/team/gregoire-header.jpg

Ainsi, si l’image originale est accessible, l’internaute n’a pas connaissance que l’image originale est accessible sur jolicode.com. Thumbor ne propose pas nativement de système d’alias.

Sur le Nginx qui héberge Thumbor, nous avons une règle de réécriture d’URL pour faire l’opération inverse :

location / { # replace /jolicode/ by /jolicode.com/ rewrite (.*)(/jolicode/)(.*) $1/jolicode.com/$3; proxy_pass http://thumbor; }

Limites de Thumbor

Nous sommes pleinement satisfaits de cette solution. Néanmoins, Thumbor a quelques limites :

Stockage des images

Par défaut, les images sont stockées sur le disque (image source et images redimensionnées). Il existe des plugins afin de pouvoir stocker les images dans Redis, MongoDB, AWS mais ils semblent peu maintenus. Nous avons utilisé le stockage sur le disque. Nous disposons de 3 serveurs. Chaque serveur va disposer de sa propre copie des images. Notre hébergeur n’a pas souhaité mettre en place un espace partagé entre les 3 serveurs.

Le second inconvénient lié à l’utilisation d’un stockage en fichier est que Thumbor n’est pas capable de purger automatiquement les images qui ont expirées. C’est à vous de mettre en place un script qui va nettoyer régulièrement les images.

Le script que nous utilisons ressemble à ça :

#!/bin/bash # Suppression des fichiers de plus de 3 jours ainsi que les répertoires vides find /data/thumbor/storage -type f -mtime +3 -delete 1>&2 >>/dev/null find /data/thumbor/result_storage -type f -mtime +3 -delete 1>&2 >>/dev/null find /data/thumbor/storage -type d -empty -delete 1>&2 >>/dev/null find /data/thumbor/result_storage -type d -empty -delete 1>&2 >>/dev/null

Gestion du cache

Thumbor va ajouter les entêtes de cache HTTP mais la mise en place d’un proxy cache est de votre responsabilité. Dans notre cas, notre client dispose d’un CDN (Akamai) et d’un Varnish. Nous vous recommandons vivement de mettre en place un proxy cache devant vos serveurs Thumbors.

Données EXIF

Les images contiennent parfois des données EXIF qu’il est important de conserver (auteur, copyright, …). Il est possible de préciser à Thumbor de conserver les données EXIF lorsqu’il redimensionne une image. Cela passe par l’option :

PRESERVE_EXIF_INFO = True

En activant cette option, la mauvaise nouvelle, c’est que Thumbor (en réalité, OpenCV) ne conserve qu’une partie des données EXIF (les données standardisées). Un des partenaires de notre client récupérait certaines entrées des données EXIF qui étaient malheureusement supprimées par Thumbor. Nous avons dû trouver un mécanisme permettant d’exposer l’image originale afin de ne pas altérer les données EXIF.

Lorsqu’on conserve les données EXIF, le profil colorimétrique des images est conservé. Il est possible de supprimer le profil colorimétrique via le filtre « strip_icc ».

Alternatives à Thumbor

Il existe de nombreuses alternatives à Thumbor qui feront peut-être l’objet d’autres articles.

Self-hosted :

imageflow écrit en Rust

Flyimg écrit en PHP

Imagine

…

SaaS

Conclusion

Thumbor est un super outil qui nous a grandement aidé dans le déploiement d’un service de redimensionnement d’images. L’intégration à Symfony a été simple grâce à la librairie phumbor. C’est une brique d’infrastructure qui pourrait facilement être remplacée par un service tiers si nécessaire.

Si vous souhaitez en savoir plus sur le sujet de l’optimisation des images, je vous invite grandement à lire “Essential Image Optimization” éditée par les équipes de performance web de Google. Et vous ? Quelle est votre stratégie pour redimensionner vos images ?