📚 Fiche de Révision - Séance 13

Thème : Upload de Fichiers et Gestion des Images

📤 1. Upload de Fichiers HTML

Formulaire d'Upload

Pour permettre aux utilisateurs de charger des fichiers, vous devez :

  1. Ajouter enctype="multipart/form-data" à la balise <form>
  2. Créer un input de type file
  3. Optionnellement, utiliser l'attribut accept pour limiter les types de fichiers

Exemple Complet

<form method="POST" action="upload.php" enctype="multipart/form-data">
    <input 
        type="file" 
        name="image" 
        accept="image/png, image/jpeg, image/jpg, image/gif"
        required
    >
    <button type="submit">Uploader</button>
</form>
⚠️ Important : Sans enctype="multipart/form-data", le fichier ne sera pas transmis au serveur !

Attribut accept

Limite les types de fichiers visibles dans le sélecteur :

Type Valeur accept
Images PNG, JPG, GIF accept="image/png, image/jpeg, image/gif"
Tous les fichiers accept="*/*"
PDF uniquement accept=".pdf"

📋 2. La Superglobale $_FILES

Structure de $_FILES

Lorsqu'un fichier est uploadé, PHP remplit le tableau $_FILES avec les informations suivantes :

// Pour un input name="image"
$_FILES["image"] = [
    "name"     => "photo.jpg",           // Nom original du fichier
    "type"     => "image/jpeg",          // Type MIME
    "tmp_name" => "/tmp/php7x9k8l",      // Chemin temporaire
    "error"    => 0,                     // Code d'erreur (0 = OK)
    "size"     => 45230                  // Taille en bytes
]

Codes d'Erreur $_FILES["error"]

Code Signification
0 OK - Fichier uploadé avec succès
1 Fichier trop volumineux (dépasse upload_max_filesize)
2 Fichier trop volumineux (dépasse MAX_FILE_SIZE)
3 Fichier partiellement uploadé
4 Aucun fichier n'a été uploadé
6 Dossier temporaire manquant
7 Impossible d'écrire le fichier

Récupérer les Informations

// Vérifier que le fichier existe
if (isset($_FILES["image"])) {
    $name = $_FILES["image"]["name"];
    $type = $_FILES["image"]["type"];
    $tmpName = $_FILES["image"]["tmp_name"];
    $error = $_FILES["image"]["error"];
    $size = $_FILES["image"]["size"];
}

🔍 3. Validations d'Upload

Vérification de l'Erreur

if ($error !== 0) {
    die("Erreur lors de l'upload. Code: " . $error);
}

Vérification du Type MIME

$typesAcceptes = [
    "image/jpeg",
    "image/png", 
    "image/gif"
];

if (!in_array($type, $typesAcceptes)) {
    die("Type de fichier non accepté : " . $type);
}

Vérification de la Taille

$maxSize = 10000000; // 10 MB

if ($size > $maxSize) {
    die("Fichier trop volumineux. Taille max: " . $maxSize . " bytes");
}

Vérification des Dimensions

// Récupérer largeur et hauteur
list($width, $height) = getimagesize($tmpName);

$maxDimension = 5000;

if ($width > $maxDimension || $height > $maxDimension) {
    die("Dimensions trop grandes. Max: " . $maxDimension . "x" . $maxDimension);
}
💡 Conseil : Toujours faire une validation côté serveur, même si l'attribut accept est utilisé côté client.

💾 4. Sauvegarder le Fichier

Générer un Nom Unique

Pour éviter les conflits de noms, générez un nom unique :

// Récupérer l'extension
$extension = pathinfo($name, PATHINFO_EXTENSION);

// Créer un nom unique avec time()
$nom_image = time() . "_" . $name;
// Résultat: "1673456789_photo.jpg"

// Ou plus sûr: juste time() + extension
$nom_image = time() . "." . $extension;
// Résultat: "1673456789.jpg"

Utiliser move_uploaded_file()

La fonction move_uploaded_file() déplace le fichier du répertoire temporaire vers son emplacement final :

$cheminFinal = "../saved_img/" . $nom_image;

if (move_uploaded_file($tmpName, $cheminFinal)) {
    echo "Fichier sauvegardé avec succès!";
} else {
    echo "Erreur lors de la sauvegarde du fichier.";
}

Chemins Relatifs depuis api/upload_image.php

Dossier Cible Chemin depuis api/
saved_img/ ../saved_img/
uploads/ ../uploads/
images/temp/ ../images/temp/
⚠️ Attention : Vérifiez que le dossier cible existe et que PHP a les permissions d'écriture !

🗄️ 5. Stocker en Base de Données

Insérer le Fichier en BDD

// Après move_uploaded_file() réussi
$sql = "INSERT INTO images (nom_fichier) VALUES (?)";
$request = BDD::get()->prepare($sql);
$request->execute([$nom_image]);

echo "Image sauvegardée en BDD!";

Afficher les Images

Sur la page index.php, récupérez et affichez les images :

<?php
// Récupérer toutes les images
$sql = "SELECT nom_fichier FROM images";
$request = BDD::get()->prepare($sql);
$request->execute();
$images = $request->fetchAll();
?>

<!-- Afficher les images -->
<div class="galerie">
    <?php foreach ($images as $image): ?>
        <img src="saved_img/<?= htmlspecialchars($image['nom_fichier']) ?>" 
             alt="Image" 
             style="max-width: 300px; margin: 10px;">
    <?php endforeach; ?>
</div>
✅ Important : Utilisez htmlspecialchars() pour éviter les injections XSS !

📝 6. Processus Complet d'Upload

Checklist

  1. ✓ Ajouter enctype="multipart/form-data" au formulaire
  2. ✓ Vérifier que $_FILES["image"] existe
  3. ✓ Vérifier que error === 0
  4. ✓ Vérifier le type MIME
  5. ✓ Vérifier la taille du fichier
  6. ✓ Récupérer les dimensions avec getimagesize()
  7. ✓ Générer un nom unique
  8. ✓ Récupérer l'extension
  9. ✓ Utiliser move_uploaded_file()
  10. ✓ Insérer en BDD
  11. ✓ Afficher avec chemin correct

Code Complet

// upload_image.php
include "myBDD.php";

if (isset($_FILES["image"])) {
    $name = $_FILES["image"]["name"];
    $type = $_FILES["image"]["type"];
    $tmpName = $_FILES["image"]["tmp_name"];
    $error = $_FILES["image"]["error"];
    $size = $_FILES["image"]["size"];
    
    // Validations
    if ($error !== 0) die("Erreur: " . $error);
    
    $typesOK = ["image/jpeg", "image/png", "image/gif"];
    if (!in_array($type, $typesOK)) die("Type invalide");
    
    if ($size > 10000000) die("Fichier trop volumineux");
    
    list($w, $h) = getimagesize($tmpName);
    if ($w > 5000 || $h > 5000) die("Dimensions trop grandes");
    
    // Sauvegarder
    $extension = pathinfo($name, PATHINFO_EXTENSION);
    $nom_image = time() . "." . $extension;
    $cheminFinal = "../saved_img/" . $nom_image;
    
    if (move_uploaded_file($tmpName, $cheminFinal)) {
        $sql = "INSERT INTO images (nom_fichier) VALUES (?)";
        BDD::get()->prepare($sql)->execute([$nom_image]);
        header("Location: ../index.php?success=1");
    }
}

🔒 7. Sécurité

⚠️ Points Critiques :
  • Vérifier TOUJOURS le type MIME côté serveur
  • Limiter la taille des fichiers
  • Générer des noms uniques pour éviter les écrasements
  • Vérifier les dimensions pour les images
  • Utiliser htmlspecialchars() pour l'affichage
  • Stocker les fichiers en dehors du webroot si possible
  • Utiliser HTTPS en production