Files
lmnp-organizer/docs/research/issue-7-maintainable-image-stack.md
T
nessar 878da155bd chore: add husky pre-commit hook and lint-staged configuration
- Created a pre-commit hook in .husky to run lint-staged.
- Added .lintstagedrc.mjs to format frontend files with Prettier and apply Spotless to backend Java files.
- Introduced .prettierignore to exclude specific directories from formatting.
2026-08-17 14:18:16 +02:00

13 KiB
Raw Blame History

Issue #7 — socle pérenne de normalisation des photos

Recherche arrêtée au 23 juillet 2026. Les affirmations techniques ci-dessous reposent sur la documentation et les dépôts officiels des projets.

Conclusion

Pour éviter de dépendre de petits wrappers Java et de binaires natifs embarqués dans des JAR, le choix le plus durable et le plus simple est ImageMagick 7 en ligne de commande, installé dans l'image Docker et invoqué par Java avec ProcessBuilder.

Après une lecture contrôlée du format, du nombre d'images et des dimensions orientées, une seule commande de conversion couvre le pipeline décidé pour l'issue #7. L'adaptateur choisit 1920x1080> pour un paysage ou un carré et 1080x1920> pour un portrait :

magick <entrée>[0] -auto-orient -resize <cadre-orienté> -strip -orient Undefined -quality 75 -define webp:lossless=false webp:<sortie>

Dans ProcessBuilder, chaque élément est un argument distinct : il ne faut pas passer cette chaîne à un shell. Les chemins d'entrée et de sortie doivent être des chemins temporaires produits par l'application, pas des noms fournis directement par l'utilisateur.

Cette commande :

  • applique l'une des orientations EXIF avant le redimensionnement ;
  • conserve le ratio dans une boîte de 1920 × 1080 en paysage ou 1080 × 1920 en portrait et, grâce au suffixe >, ne redimensionne que les images plus grandes ;
  • supprime profils, commentaires, EXIF, XMP et chunks de métadonnées connus, puis efface explicitement toute orientation résiduelle ;
  • écrit un WebP avec pertes de qualité 75 ;
  • sélectionne la première image avec [0]. Accepter, rejeter ou aplatir les formats animés reste une décision produit, pas une conséquence d'ImageMagick.

Sources : auto-orientation, qualité, redimensionnement et suppression des métadonnées, géométrie WxH> et conservation du ratio, options de l'encodeur WebP.

Pourquoi ce choix répond au risque de maintenance

ImageMagick n'est pas une petite bibliothèque Java :

  • le projet est maintenu par ImageMagick Studio LLC ;
  • son dépôt compte plus de 24 000 commits et 277 publications ;
  • la version 7.1.2-27 a été publiée le 5 juillet 2026 ;
  • le projet publie des versions Linux, macOS et Windows et possède un canal d'avis de sécurité actif ;
  • le support WebP s'appuie sur la bibliothèque libwebp du système, au lieu d'enfermer une copie dans le JAR de l'application.

Sources : organisation ImageMagick Studio LLC, dépôt et historique des versions, téléchargements officiels, avis de sécurité officiels.

Aucun projet ne garantit qu'il sera maintenu éternellement. Ici, le risque est toutefois limité par deux propriétés : le projet est ancien, très diffusé et activement publié ; l'intégration applicative repose sur un contrat de processus minuscule, sans types ni API Java propres à ImageMagick. Un éventuel remplacement ne toucherait que l'appel de normalisation et l'image Docker.

Sécurité des uploads non fiables

La contrepartie d'ImageMagick est sa grande surface fonctionnelle. L'installation par défaut, ouverte, ne doit pas traiter directement les uploads. Le projet fournit précisément une politique pour réduire cette surface :

  • refuser tous les modules puis autoriser seulement les formats d'entrée retenus et l'écriture WebP ;
  • interdire tous les délégués externes et filtres ;
  • interdire les lectures indirectes (@...), les flux standard et les chemins sensibles ;
  • limiter largeur, hauteur et longueur des séquences ;
  • borner mémoire, mémoire mappée, disque temporaire, threads et durée ;
  • utiliser un répertoire temporaire privé ;
  • vérifier la politique chargée avec magick identify -list policy.

ImageMagick documente des limites distinctes pour la mémoire, le mmap, le disque, la durée, la largeur, la hauteur et le nombre d'images. La limite area ne rejette pas une image : elle fait basculer son cache de pixels vers le disque. Les valeurs d'exemple de la documentation (256MiB, 4KP, etc.) ne sont pas des recommandations pour LMNP Organizer ; elles devront être choisies selon la mémoire réelle du conteneur et la concurrence mesurée. Le timeout Java et la limite mémoire du conteneur restent la dernière barrière.

Source : politique de sécurité ImageMagick et exemple complet, ressources et variables de limite.

Comparaison

Option Pipeline complet Pérennité observable Sécurité et ressources Coût d'intégration Verdict
ImageMagick 7 CLI Oui, en une commande ImageMagick Studio LLC, 277 versions, publication active en juillet 2026 Politique fine : formats, chemins, délégués, mémoire, disque, dimensions, séquences, temps Un paquet système, un appel ProcessBuilder, un policy.xml Recommandé
libvips / vipsthumbnail Oui, en une commande Projet actif, 102 versions, utilisé notamment par Mastodon, Sharp, Rails et MediaWiki Faible mémoire, blocage des opérations non fiables et dimension maximale ; garde-fous moins complets qu'ImageMagick au niveau CLI Un paquet système et un appel ProcessBuilder Excellente alternative si les mesures montrent qu'ImageMagick consomme trop
cwebp officiel Non Implémentation de référence WebP publiée par Google, API documentée en version 1.6.0 Option -low_memory, mais pas de politique complète de décodage/upload Nécessite du code ou un second outil pour lire l'orientation et calculer la taille Ne pas retenir seul
Bibliothèques Java ImageIO Non TwelveMonkeys est établi et actif Décodage dans le processus JVM ; pas de politique équivalente WebP TwelveMonkeys est en lecture seule ; un autre encodeur reste nécessaire Ne résout pas le problème de dépendance

Détail : libvips

vipsthumbnail sait réaliser la même transformation une fois le cadre orienté choisi :

vipsthumbnail <entrée> --size <cadre-orienté> --path <sortie>.webp[Q=75,keep=none]

La documentation garantit que :

  • la sortie tient dans la boîte demandée ;
  • le suffixe > limite le redimensionnement aux images plus grandes ;
  • l'orientation des métadonnées est appliquée par défaut ;
  • keep=none ne conserve aucune métadonnée ;
  • le writer WebP est avec pertes par défaut et Q accepte 0 à 100 ;
  • les formats disponibles dépendent des codecs présents à la compilation, notamment libwebp et libheif.

libvips est un projet C majeur, pas un petit wrapper : son dépôt officiel le donne comme moteur de Mastodon, Sharp, imgproxy, Rails et MediaWiki ; il publie encore régulièrement des correctifs issus du fuzzing. Il expose VIPS_BLOCK_UNTRUSTED pour bloquer les opérations dépendant de bibliothèques non durcies et VIPS_MAX_COORD pour borner une dimension. Il consomme généralement moins de mémoire qu'ImageMagick.

Il reste en second choix ici parce que la demande priorise la notoriété et la pérennité perçue, et parce qu'ImageMagick offre une politique de sécurité plus complète directement adaptée à un service d'upload. Si un benchmark réel révèle un problème de mémoire ou de débit, remplacer la commande par vipsthumbnail est une évolution localisée.

Sources : guide officiel vipsthumbnail, thumbnail : orientation, boîte et absence d'agrandissement, écriture WebP, formats, utilisateurs et dépendances officielles, blocage des opérations non fiables, versions officielles.

Détail : cwebp

cwebp, livré avec la bibliothèque de référence libwebp, est l'encodeur le plus directement gouverné par le projet WebP. Il accepte PNG, JPEG, TIFF, WebP et Y'CbCr brut ; -q 75 produit un WebP avec pertes ; -metadata none est le comportement par défaut ; -resize peut redimensionner et préserver le ratio si une dimension vaut zéro.

Il ne fournit cependant pas l'auto-orientation EXIF et son redimensionnement reçoit une largeur et une hauteur calculées. Pour respecter simultanément l'orientation, la boîte 1920 × 1080 et l'absence d'agrandissement, l'application devrait ajouter un parseur EXIF et du code de transformation, ou lancer un deuxième outil. Ce serait plus de code et deux chaînes de traitement alors qu'ImageMagick ou libvips le font seuls.

Sources : documentation officielle de cwebp, API officielle libwebp 1.6.0, libwebp comme implémentation de référence.

Absence de solution Java établie et suffisante

Le JDK 25 n'écrit pas WebP. TwelveMonkeys est l'extension ImageIO Java la plus établie examinée, mais son tableau officiel indique explicitement que son plugin WebP sait lire et ne sait pas écrire. Il offre un bon redimensionneur, mais il faudrait toujours un encodeur natif ou un autre projet.

Une grosse abstraction Java par-dessus un outil natif n'améliore donc pas la pérennité : elle ajoute un mainteneur et une API à suivre sans retirer la dépendance au codec. Appeler directement un exécutable maintenu et versionné dans Docker est plus simple et rend le cycle de correctifs natifs indépendant des dépendances Maven.

Sources : formats supportés par TwelveMonkeys, formats garantis par ImageIO dans Java 25.

Intégration minimale recommandée

  1. Installer ImageMagick 7 avec le codec WebP dans l'étage d'exécution Docker ; vérifier au build la version et WEBP avec magick -version et magick identify -list format.
  2. Ajouter un policy.xml en refus par défaut et n'autoriser que les codecs décidés. Ne pas recopier des seuils arbitraires : les mesurer puis les fixer.
  3. Écrire l'upload dans un fichier temporaire contrôlé par l'application.
  4. Exécuter la commande via les arguments de ProcessBuilder, sans shell, avec délai d'expiration et destruction forcée du processus.
  5. Exiger le code de sortie zéro, rouvrir le résultat, vérifier qu'il est WebP et dans la boîte 1920 × 1080, puis seulement publier la référence canonique et supprimer l'original.
  6. Couvrir dans l'image Docker réelle les huit orientations EXIF, portrait/paysage, absence d'agrandissement, qualité avec pertes, suppression des métadonnées, flux invalide et arrêt sur dépassement des limites.

Cette décision remplace la proposition Thumbnailator + usefulness/webp-imageio ; elle ne fixe ni les formats d'entrée du contrat utilisateur, ni les seuils de ressources.

Calibration retenue pour le conteneur

Le 23 juillet 2026, le pipeline final a été mesuré dans l'image d'exécution limitée à 1 Gio et 2 CPU :

  • une photo JPEG représentative de 4 032 × 3 024 pixels est devenue un WebP de 1 440 × 1 080 en 0,40 s ;
  • deux JPEG adversariaux de 8 000 × 6 000 pixels (environ 22 Mio chacun) traités simultanément sont devenus des WebP de 1 440 × 1 080 en 8,88 s et 8,97 s.

Ces mesures motivent les limites applicatives de 192 Mio de mémoire, 384 Mio de mémoire mappée, 768 Mio de disque temporaire, deux threads et 20 secondes par processus, sous les plafonds plus stricts du conteneur et de policy.xml. Elles devront être reprises si la mémoire, le nombre de CPU ou la concurrence du conteneur changent.