Aller au contenu

Compression des images produit

Points d’entrée

Lors de ProductController::create() et ProductController::update(), le champ non mappé image du formulaire ProductType est transmis à :

UploaderService::uploadImageCompressed(
    $file,
    $this->getParameter('product_directory')
);

Le répertoire cible est public/uploads/products/sale. Le même service est utilisé pour les images de stock d’entrepôt dans ProductStockController.

Prérequis

  • extension PHP GD ;
  • droits d’écriture sur le répertoire cible ;
  • mémoire PHP suffisante pour décoder l’image originale et créer le bitmap redimensionné.

La mémoire consommée dépend surtout des dimensions décodées, pas uniquement du poids du fichier compressé.

Algorithme

Valeurs par défaut :

Paramètre Valeur
dimension maximale 1600 px
qualité JPEG initiale 82
qualité minimale 55
taille cible 2 000 000 octets

Traitement :

  1. Lire le MIME détecté côté serveur.
  2. Générer un nom slugifié et unique terminé par .jpg.
  3. Décoder JPEG, PNG ou WebP avec GD.
  4. Calculer un facteur conservant le ratio.
  5. Ne réduire que si largeur ou hauteur dépasse 1600 px.
  6. Créer une image RGB aux nouvelles dimensions.
  7. Appliquer un fond blanc, puis rééchantillonner l’original.
  8. Encoder en JPEG progressif.
  9. Essayer successivement les qualités 82, 78, 72, 66, 60, 55, sans doublon.
  10. Arrêter dès que le fichier est valide et inférieur ou égal à 2 000 000 octets.
  11. Libérer les ressources GD et enregistrer uniquement le nom dans l’entité.

Le paramètre initial est borné entre 55 et 95. Si une autre qualité est passée, elle devient la première tentative.

Conversion et transparence

Toutes les images traitées sont converties en JPEG. La transparence PNG ou WebP est donc perdue et remplacée par un fond blanc. Le profil colorimétrique et les métadonnées EXIF ne sont pas explicitement conservés. L’orientation EXIF n’est pas corrigée.

Cas de repli

uploadFile() est utilisé sans compression lorsque :

  • le MIME n’est pas une image ;
  • le format n’est pas JPEG, PNG ou WebP ;
  • GD ne supporte pas WebP ;
  • le décodage échoue ;
  • l’écriture JPEG échoue ou produit un fichier vide.

Dans ce cas, l’extension et le contenu d’origine sont conservés. Le fallback rend l’ajout plus tolérant, mais signifie que la compression et la limite cible ne sont pas garanties.

Limite actuelle de 2 Mo

La taille de 2 Mo est une cible, pas une validation stricte. Si le dernier JPEG de qualité 55 reste supérieur à 2 Mo, le code le conserve tout de même dès lors que le fichier existe et n’est pas vide.

Pour garantir la limite, il faudrait :

  • réduire à nouveau les dimensions après la qualité minimale ; ou
  • supprimer le fichier et lever une exception si la cible reste dépassée.

Validation du formulaire

Le champ image de ProductType est facultatif et non mappé. Il ne définit actuellement aucune contrainte Symfony File sur :

  • la taille maximale de l’upload ;
  • les MIME autorisés ;
  • les dimensions minimales ou maximales.

La détection dans UploaderService ne remplace pas une validation de formulaire. Il est recommandé d’ajouter Image ou File, de limiter la taille originale et de rejeter explicitement les formats non supportés.

Cohérence et cycle de vie

  • La création et la route principale de mise à jour compressent l’image.
  • Certains autres flux historiques, notamment le réajustement depuis la page de détail, appellent encore uploadFile() et peuvent stocker une image non compressée.
  • Le remplacement d’une image ne supprime pas automatiquement l’ancien fichier, ce qui peut produire des fichiers orphelins.
  • L’upload est effectué avant le flush Doctrine. Si la transaction métier échoue ensuite, le fichier peut rester présent sans référence en base.

Diagnostic

En cas d’échec :

  1. vérifier ext-gd et le support du format ;
  2. contrôler memory_limit, upload_max_filesize et post_max_size ;
  3. vérifier les permissions du répertoire ;
  4. inspecter les dimensions de l’original ;
  5. vérifier si le fallback a conservé l’extension d’origine ;
  6. contrôler l’espace disque et les fichiers orphelins.