Aller au contenu
M07 · B. Programmer en PHP

Manipuler des fichiers : lire, écrire, envoyer, supprimer, télécharger

Lire et écrire des fichiers texte et CSV sur le serveur, recevoir un fichier envoyé par un formulaire ($_FILES, move_uploaded_file) en le contrôlant, le supprimer (unlink) et le proposer en téléchargement.

1Situer

Les artisans de Souk Atlas veulent envoyer la photo de leurs produits, les clients laisser un avis, et le gérant télécharger la liste des commandes en CSV pour Excel. Toutes ces fonctions passent par des fichiers stockés sur le serveur, que PHP lit, écrit et déplace.

2Comprendre

Le trajet d'un fichier envoyé
photo du produit tajine.jpg dossier temporaire /tmp/phpA1b2c3 vide (fichier déplacé) $_FILES['photo'] name => 'tajine.jpg' size => 184320 error => 0 tmp_name => '/tmp/phpA1b2c3' ✔ error = 0 ✔ 180 Ko < 2 Mo ✔ image/jpeg uploads/ p_65f2a9.jpg

Le formulaire a enctype="multipart/form-data" et un champ type="file". Le visiteur choisit tajine.jpg.

1 / 5

Lire et écrire un fichier en une ligne

$fichier = __DIR__ . '/compteur.txt';
$visites = file_exists($fichier) ? (int) file_get_contents($fichier) : 0;
$visites++;
file_put_contents($fichier, $visites);
echo "Visite n° $visites";

file_put_contents(__DIR__ . '/journal.txt', date('H:i') . " connexion\n", FILE_APPEND);
$lignes = file(__DIR__ . '/journal.txt', FILE_IGNORE_NEW_LINES);   // un tableau, une case par ligne

D’autres fonctions utiles : file_exists, is_file, is_dir, filesize, mkdir, scandir (liste d’un dossier), copy, rename, unlink (supprimer).

Ouvrir, lire ligne par ligne, fermer

$f = fopen(__DIR__ . '/commandes.csv', 'w');
fputcsv($f, ['numero', 'client', 'total'], ';');
fputcsv($f, [1042, 'Amina Benali', 330], ';');
fclose($f);

Recevoir un fichier envoyé (upload)

<form action="envoyer.php" method="post" enctype="multipart/form-data">
  <input type="file" name="photo" accept="image/*">
  <button>Envoyer</button>
</form>

Les contrôles indispensables, dans cet ordre :

  1. $_FILES['photo']['error'] === UPLOAD_ERR_OK (sinon : pas de fichier, trop gros pour upload_max_filesize…) ;
  2. la taille (size) ;
  3. le type réel du contenu (mime_content_type($tmp) ou finfo), pas l’extension ni le type annoncé, que le visiteur contrôle ;
  4. un nouveau nom généré par le serveur (bin2hex(random_bytes(8)) . '.jpg'), jamais le nom d’origine tel quel.

Supprimer et proposer un téléchargement

<?php // export.php
$fichier = __DIR__ . '/../prive/commandes.csv';     // hors de htdocs : pas d'accès direct
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="commandes-' . date('Y-m-d') . '.csv"');
header('Content-Length: ' . filesize($fichier));
readfile($fichier);
exit;

Pour supprimer : unlink($chemin) (renvoie true si c’est fait).

Exemple simple

Le cahier de réclamations

Chaque client écrit sa remarque à la suite des autres, sans effacer les pages précédentes : c'est file_put_contents avec FILE_APPEND.

Exemple simple

Le dépôt au guichet

On dépose un colis : l'agent le pose d'abord sur un comptoir provisoire (le dossier temporaire), vérifie le poids et le contenu, puis le range dans le bon casier (move_uploaded_file). S'il est refusé, le comptoir est vidé à la fermeture.

3Manipuler

Un livre d'or enregistre les avis dans avis.txt. Complétez ecrire.php, puis laissez un avis depuis le navigateur. Le fichier export.php doit proposer le fichier en téléchargement.
Pas à pas dans le lab ↗ Cliquez sur Exécuter : PHP 8.3 démarre dans votre navigateur (environ 5 Mo à télécharger, une seule fois).

La page s'affichera ici.

Le navigateur demandera /index.php au serveur, qui exécutera le PHP et renverra du HTML.

Entre le navigateur et le serveur aucun échange pour l'instant

Consignes 0 / 3

  1. Deux avis envoyés à la suite apparaissent tous les deux (« 2 avis »)
  2. Le fichier avis.txt existe sur le serveur après un envoi
  3. export.php envoie Content-Disposition: attachment avec le nom avis-souk.txt

4Pratiquer

Exercice 1 · Lire un CSV

Le fichier produits.csv contient des lignes « nom;prix;stock » avec une ligne de titres. Affichez chaque produit avec fgetcsv.

Voir le corrigé
<?php
$f = fopen(__DIR__ . '/produits.csv', 'r');
fgetcsv($f, null, ';');                      // on saute la ligne des titres
while (($ligne = fgetcsv($f, null, ';')) !== false) {
    [$nom, $prix, $stock] = $ligne;
    echo htmlspecialchars($nom) . " : $prix DH ($stock en stock)<br>";
}
fclose($f);

Exercice 2 · Recevoir une photo

Écrivez le traitement d'un formulaire d'envoi de photo (champ photo) : vérifiez l'absence d'erreur, une taille de 2 Mo maximum, un type JPEG ou PNG, puis rangez le fichier dans uploads/ avec un nom unique.

Voir le corrigé
<?php
$f = $_FILES['photo'] ?? null;
if (!$f || $f['error'] !== UPLOAD_ERR_OK) {
    exit("Envoi échoué.");
}
if ($f['size'] > 2 * 1024 * 1024) {
    exit("Fichier trop lourd (2 Mo maximum).");
}
$type = mime_content_type($f['tmp_name']);           // type réel, lu dans le fichier
$extensions = ['image/jpeg' => 'jpg', 'image/png' => 'png'];
if (!isset($extensions[$type])) {
    exit("Seules les images JPEG et PNG sont acceptées.");
}
$nom = bin2hex(random_bytes(8)) . '.' . $extensions[$type];
move_uploaded_file($f['tmp_name'], __DIR__ . '/uploads/' . $nom);
echo "Photo enregistrée : uploads/$nom";

Exercice 3 · Supprimer une photo

supprimer.php?fichier=abc.jpg doit supprimer uploads/abc.jpg. Écrivez-le sans permettre de supprimer un autre fichier du serveur.

Voir le corrigé
<?php
$nom = basename($_GET['fichier'] ?? '');      // retire tout chemin : ../config.php devient config.php
$chemin = __DIR__ . '/uploads/' . $nom;
if ($nom !== '' && is_file($chemin)) {
    unlink($chemin);
    echo "Supprimé.";
} else {
    echo "Fichier introuvable.";
}

5Vérifier

1. Quel attribut est indispensable sur un formulaire qui envoie un fichier ?
2. Que fait file_put_contents('log.txt', $texte) si log.txt contient déjà des lignes ?
3. Où se trouve un fichier envoyé juste après la requête, avant move_uploaded_file ?