12 - Upload d'Images et Redimensionnement (Symfony 7) (2024)

Temps de lecture : 23 minutes environ.

Dans ce 12e tutoriel de la série Open Blog sur Symfony 7, nous allons nous concentrer sur l'upload d'images dans notre blog multi-utilisateur. L'objectif est d'apprendre à insérer des images dans notre base de données et sur notre serveur, en nous assurant que tout fonctionne correctement.

Préparation de l'Environnement

Avant de pouvoir gérer des fichiers d'image dans Symfony, il est nécessaire de s'assurer que toutes les bibliothèques requises sont installées sur le serveur. C'est pourquoi nous avons modifié le Dockerfile pour inclure les bibliothèques essentielles :

  • build-essential : Fournit les outils nécessaires pour compiler des logiciels depuis leur code source.
  • libjpeg-dev : Librairie de développement pour manipuler les fichiers JPEG.
  • libwebp-dev : Librairie pour gérer les images WebP.
  • libfreetype6-dev : Librairie pour manipuler les polices de caractères, souvent utilisée avec les images.

Ces librairies sont cruciales pour le traitement des images, notamment pour la création de miniatures ou le redimensionnement.

Configuration des Extensions PHP

Ensuite, nous activons et configurons les extensions PHP nécessaires pour gérer les images. L'extension GD est particulièrement importante, car elle permet de manipuler des images directement en PHP.

FROM php:8.2-apache

RUN apt-get update \
&& apt-get install -y zlib1g-dev g++ git libicu-dev zip libzip-dev zip \
&& apt-get install -y build-essential curl zlib1g-dev g++ git libicu-dev zip libzip-dev libpng-dev libjpeg-dev libwebp-dev libfreetype6-dev \
&& docker-php-ext-install intl opcache pdo pdo_mysql \
&& pecl install apcu \
&& docker-php-ext-enable apcu \
&& docker-php-ext-configure zip \
&& docker-php-ext-install zip

RUN docker-php-ext-configure gd --with-freetype --with-webp --with-jpeg \
&& docker-php-ext-install gd \
&& docker-php-ext-install exif

RUN a2enmod rewrite && a2enmod ssl && a2enmod socache_shmcb

WORKDIR /var/www

Création du Service PictureService

Le service PictureService est au cœur de notre gestion des images. Il encapsule la logique nécessaire pour manipuler les images uploadées, telles que les redimensionner ou les convertir dans un autre format.

Structure du Service

Voici la structure du service que nous avons créée :

  • Namespace : App\Service – Tous les services de l'application sont regroupés dans ce namespace.
  • Constructor : Nous utilisons le ParameterBagInterface pour injecter les paramètres configurés dans services.yaml, notamment le chemin d'upload.
  • Méthode square : Cette méthode prend une image uploadée en entrée, la redimensionne pour qu'elle soit carrée, et la sauvegarde sur le serveur.

Gestion des Dimensions et Recadrage

La méthode square fonctionne comme suit :

  1. Extraction des dimensions : Nous utilisons getimagesize pour obtenir les dimensions de l'image et déterminer son type (JPEG, PNG, WebP).
  2. Création de l'image source : Selon le type de l'image, nous utilisons la fonction correspondante (imagecreatefromjpeg, imagecreatefrompng, imagecreatefromwebp) pour créer une ressource d'image en PHP.
  3. Recadrage de l'image : En fonction des dimensions, nous calculons la partie centrale de l'image pour en faire un carré. Cela implique de déterminer si l'image est en mode portrait, paysage ou déjà carrée.
  4. Création de l'image redimensionnée : Nous créons une nouvelle image carrée avec les dimensions spécifiées (par défaut 250x250 pixels).
  5. Enregistrement de l'image : Enfin, l'image est enregistrée au format WebP dans le dossier spécifié.

Gestion des Types de Fichiers

Le service vérifie également que l'image uploadée est dans un format supporté (JPEG, PNG, ou WebP). Si ce n'est pas le cas, une exception est levée.

<?php

namespace App\Service;

use Exception;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class PictureService
{
public function __construct(
private ParameterBagInterface $params
)
{}

public function square(UploadedFile $picture, ?string $folder = '', ?int $width = 250): string
{
// On donne un nouveau nom à l'image
$file = md5(uniqid(rand(), true)) . '.webp';

// On récupère les informations de l'image
$pictureInfos = getimagesize($picture);

if($pictureInfos === false){
throw new Exception('Format d\'image incorrect');
}

// On vérifie le type mime
switch($pictureInfos['mime']){
case 'image/png':
$sourcePicture = imagecreatefrompng($picture);
break;
case 'image/jpeg':
$sourcePicture = imagecreatefromjpeg($picture);
break;
case 'image/webp':
$sourcePicture = imagecreatefromwebp($picture);
break;
default:
throw new Exception('Format d\'image incorrect');
}

// On recadre l'image
$imageWidth = $pictureInfos[0];
$imageHeight = $pictureInfos[1];

switch($imageWidth <=> $imageHeight){
case -1: // portrait
$squareSize = $imageWidth;
$srcX = 0;
$srcY = ($imageHeight - $imageWidth) / 2;
break;

case 0: // carré
$squareSize = $imageWidth;
$srcX = 0;
$srcY = 0;
break;

case 1: // paysage
$squareSize = $imageHeight;
$srcX = ($imageWidth - $imageHeight) / 2;
$srcY = 0;
break;
}

// On crée une nouvelle image vierge
$resizedPicture = imagecreatetruecolor($width, $width);

// On génère le contenu de l'image
imagecopyresampled($resizedPicture, $sourcePicture, 0, 0, $srcX, $srcY, $width, $width, $squareSize, $squareSize);

// On crée le chemin de stockage
$path = $this->params->get('uploads_directory') . $folder;

// On crée le dossier s'il n'existe pas
if(!file_exists($path . '/mini/')){
mkdir($path . '/mini/', 0755, true);
}

// On stocke l'image réduite
imagewebp($resizedPicture, $path . '/mini/' . $width . 'x' . $width . '-' . $file);

// On stocke l'image originale
$picture->move($path . '/', $file);

return $file;
}
}

3. Intégration du Service dans le Contrôleur

Le contrôleur utilise ce service pour gérer les images uploadées lors de la création d'un nouvel article. Voici comment cela fonctionne :

  1. Récupération du formulaire : Lorsqu'un utilisateur soumet un formulaire pour ajouter un article, Symfony récupère les données du formulaire, y compris l'image.
  2. Traitement de l'image : Si une image est présente, elle est passée au service PictureService pour être redimensionnée et sauvegardée.
  3. Enregistrement en base de données : Le nom du fichier généré est ensuite stocké dans la base de données, associé à l'article.
 #[Route('/ajouter', name: 'add')]
public function addArticle(
Request $request,
SluggerInterface $slugger,
EntityManagerInterface $em,
UsersRepository $usersRepository,
PictureService $pictureService
): Response
{
$post = new Posts();

$form = $this->createForm(AddPostFormType::class, $post);

$form->handleRequest($request);

if($form->isSubmitted() && $form->isValid()){
$post->setSlug(strtolower($slugger->slug($post->getTitle())));

$post->setUsers($this->getUser());

$featuredImage = $form->get('featuredImage')->getData();

$image = $pictureService->square($featuredImage, 'articles', 300);

$post->setFeaturedImage($image);

$em->persist($post);
$em->flush();

$this->addFlash('success', 'L\'article a été créé');
return $this->redirectToRoute('app_profile_posts_index');
}

return $this->render('profile/posts/add.html.twig', [
'form' => $form
]);
}

4. Gestion du Frontend pour l'Upload d'Images

Pour améliorer l'expérience utilisateur, nous ajoutons une prévisualisation de l'image directement sur le frontend.

Modification du Template Twig

Nous ajoutons une balise <div> contenant une balise <img> dans le template Twig, qui sera utilisée pour afficher l'aperçu de l'image :

<div class="form-group">
{{ form_label(form.featuredImage) }}
{{ form_widget(form.featuredImage) }}
<div class="preview" style="display:none;">
<img id="imagePreview" width="300">
</div>
</div>

Création du JavaScript pour la Prévisualisation

Le fichier add_article.js est responsable de la gestion de l'affichage de la prévisualisation de l'image :

  • Listener sur le champ d'image : Nous écoutons les changements sur le champ d'image (#post_featuredImage).
  • Utilisation de FileReader : Lorsqu'une image est sélectionnée, nous utilisons FileReader pour lire l'image en tant qu'URL de données.
  • Affichage de l'image : Une fois l'image lue, elle est affichée dans l'élément <img> précédemment caché.

Ce script assure que l'utilisateur voit un aperçu de l'image sélectionnée avant de soumettre le formulaire.

document.querySelector("#add_post_form_featuredImage").addEventListener("change", checkFile);

function checkFile(){
let preview = document.querySelector(".preview");
let image = preview.querySelector("img");
let file = this.files[0];
const types = ["image/jpeg", "image/png", "image/webp"];
let reader = new FileReader();

reader.onloadend = function(){
image.src = reader.result;
preview.style.display = "block";
}

// On vérifie qu'un fichier existe
if(file){
// On vérifie que le fichier est bien une image acceptée
if(types.includes(file.type)){
reader.readAsDataURL(file);
}
}else{
image.src = "";
preview.style.display = "none"
}
}

Inclusion du JavaScript dans le Template

Le script est ensuite inclus dans le template pour s'assurer qu'il est chargé lorsque la page est rendue :

{% block importmap %}
{{ importmap(['app', 'add-article']) }}
{% endblock %}

Ces fonctionnalités assurent que les images uploadées sont correctement traitées, stockées et affichées à l'utilisateur, tout en gardant une structure de code propre et maintenable. Si vous avez des questions ou besoin de plus de détails sur certains aspects, n'hésitez pas à nous contacter sur Discord ou via les forums du projet.

Obtenir de l'aide

Pour obtenir de l'aide, vous pouvez accéder au serveur Discord pour une entraide par chat.

12 - Upload d'Images et Redimensionnement (Symfony 7) (2024)
Top Articles
Latest Posts
Article information

Author: Duncan Muller

Last Updated:

Views: 6117

Rating: 4.9 / 5 (79 voted)

Reviews: 94% of readers found this page helpful

Author information

Name: Duncan Muller

Birthday: 1997-01-13

Address: Apt. 505 914 Phillip Crossroad, O'Konborough, NV 62411

Phone: +8555305800947

Job: Construction Agent

Hobby: Shopping, Table tennis, Snowboarding, Rafting, Motor sports, Homebrewing, Taxidermy

Introduction: My name is Duncan Muller, I am a enchanting, good, gentle, modern, tasty, nice, elegant person who loves writing and wants to share my knowledge and understanding with you.