Mis à jour le jeudi 31 octobre 2013
  • Facile
Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Introduction du cours

Bien souvent, il arrive qu’on veuille récupérer le contenu d’un dossier ; or à moins de télécharger les fichiers un à un, ce n’est pas possible. Avec PHP on peut cependant créer une archive Zip et la proposer au téléchargement.

Les bases nécessaires à la compréhension de ce tutoriel sont la programmation en PHP ( :p ) et la programmation orientée objet (tutoriel de vyk12), car les fonctions zip_* ne sont pas nombreuses et ne permettent que la lecture et l’extraction d’une archive. On utilisera donc la classe ZipArchive.
Pour pouvoir utiliser cette classe, il faut que l’extension php_zip soit activée, si elle ne l’est pas il faut la décommenter dans le fichier php.ini.

Ce tutoriel servira d’introduction aux fonctions Zip.

Instancier la classe ZipArchive

Je ne suis pas là pour vous apprendre à faire de la POO donc je ne m’étendrai pas là-dessus ; les mots instance, classe, etc. doivent vous être familiers.
Le nom de la classe est ZipArchive (documentation), elle permet de gérer totalement une archive de type zip. Elle ne prend aucun paramètre à son instanciation.

<?php $zip = new ZipArchive(); ?>

Ceci va donc créer une instance de cette classe que l’on pourra manipuler tout au long du code.

Créer, ouvrir, fermer une archive

Le nom de mon instance de ZipArchive sera $zip durant tout le tutoriel.
On a instancié notre classe, on peut donc commencer à la manipuler. Il y a deux cas possibles pour l’ouverture d’une archive :

  • on crée une nouvelle archive ;

  • on ouvre une archive existante.

La méthode destinée à ouvrir une archive est <?php $zip->open(); ?> .
Si on veut créer une nouvelle archive, il faudra passer en argument la valeur ZipArchive::CREATE  qui lui indiquera qu’il faut ouvrir une nouvelle archive.
Si l’ouverture réussit, la méthode renvoie « TRUE », sinon elle renvoie un code d’erreur. Une liste complète des codes d’erreur est disponible ici.

<?php
      $zip = new ZipArchive(); 
      // On ouvre l’archive.
      if($zip->open('Zip.zip') == TRUE)
      // On crée l’archive.
      if($zip->open('Zip.zip', ZipArchive::CREATE) == TRUE)
      {
        echo '&quot;Zip.zip&quot; ouvert';
      }
      else
      {
        echo 'Impossible d&#039;ouvrir &quot;Zip.zip&quot;';
	// Traitement des erreurs avec un switch(), par exemple.
      } ?>

Le code ci-dessus ne fera strictement rien si on crée une nouvelle archive zip car celle-ci est vide et donc ne sera pas sauvegardée. De plus, on ne ferme pas la ressource que nous renvoie $zip.

<?php
      $zip = new ZipArchive(); 
      // On ouvre l’archive.
      if($zip->open('Zip.zip') == TRUE)
      // On crée l’archive.
      if($zip->open('Zip.zip', ZipArchive::CREATE) == TRUE)
      {
        echo '&quot;Zip.zip&quot; ouvert';

        $zip->close();
      }
      else
      {
        echo 'Impossible d&#039;ouvrir &quot;Zip.zip&quot;';
	// Traitement des erreurs avec un switch(), par exemple.
      } ?>

On sait donc comment créer, ouvrir et fermer une archive. Nous allons maintenant passer à l’ajout de fichiers.

Ajouter, renommer, supprimer et extraire des fichiers

Ajouter des fichiers

On dispose donc d’une archive avec le code de la partie précédente et à laquelle on va ajouter des fichiers. Commençons avec de simples fichiers texte (*.txt).
Il existe deux méthodes pour cela :

  • addFile, pour ajouter un fichier existant ;

  • addFromString, pour écrire directement le contenu.

Ces deux méthodes sont donc <?php $zip->addFile(); ?> et <?php $zip->addFromString(); ?> (elles écrasent le contenu si le fichier existe déjà), elles renvoient toutes les deux un booléen.
Notez que <?php $zip->addFile(); ?> possède un paramètre facultatif qui permet de changer le nom du fichier dans l’archive.

bool ZipArchive::addFile  ( string $filename  [, string $localname  ] )

Le contenu du fichier .txt sera très simple.

Je suis le contenu de Fichier.txt !
<?php
      $zip = new ZipArchive(); 
      if($zip->open('Zip.zip', ZipArchive::CREATE) === true)
      {
        echo '&quot;Zip.zip&quot; ouvert<br/>';
	
	// Ajout d’un fichier.
	$zip->addFile('Fichier.txt');
	
	// Ajout direct.
	$zip->addFromString('Fichier.txt', 'Je suis le contenu de Fichier.txt !');

        // Et on referme l'archive.
	$zip->close();
      }
      else
      {
        echo 'Impossible d&#039;ouvrir &quot;Zip.zip<br/>';
	// Traitement des erreurs avec un switch(), par exemple.
      }

L’ajout de fichier est donc très simple : pour de simples fichiers contenant du texte on passe par <?php $zip->addFromString(); ?> et pour ce qui est des images, des documents Word, XML, etc., on passe par <?php $zip->addFile(); ?>.

Si on veut organiser son archive, on peut y créer des dossiers, soit en passant par <?php $zip->addEmptyDir('Le nom du dossier'); ?>, soit en précisant directement un nom de dossier lors de l’ajout du fichier.

<?php $zip = new ZipArchive(); 
      if($zip->open('Zip.zip', ZipArchive::CREATE) === true)
      {
        echo '&quot;Zip.zip&quot; ouvert<br/>';
 
	// Création d'un dossier par addEmptyDir(). [Facultatif]
        $zip->addEmptyDir('Fichiers textes');
 
	// Ajout d'un fichier.
	$zip->addFile('Fichiers textes/Fichier.txt');
	
	// Ajout directement.
	$zip->addFromString('Fichiers textes/Fichier.txt', 'Je suis le contenu de Fichier.txt !');

        // Et on referme l’archive.
	$zip->close();
      }
      else
      {
        echo 'Impossible d&#039;ouvrir &quot;Zip.zip<br/>';
	// Traitement des erreurs avec un switch(), par exemple.
      }

Renommer et supprimer

Pour renommer et supprimer des fichiers, on dispose de deux méthodes. On peut soit passer le nom du fichier, soit passer son numéro d'index (récupérable par <?php $zip->locateName('nom du fichier'); ?>). Elles renvoient toutes deux un booléen indiquant le succès de l’opération.

<?php // Suppression en passant par l’index.
      $zip->deleteIndex(3);
      
      // Suppression en passant par le nom du fichier.
      $zip->deleteName('Nom du fichier.extension');
      
      // Renommer en passant par l’index.
      $zip->renameIndex(3, 'Nouveau nom.extension');
      
      // Renommer en passant par le nom.
      $zip->renameName('Fichier.extension', 'Nouveau Nom.extension'); ?>

Extraire les fichiers

Il est possible d’extraire le contenu de l’archive (seulement une/plusieurs entrée(s) ou même l’archive complète) grâce à la méthode <?php $zip->extractTo(); ?>.

<?php // Extraction unique dans le dossier courant.
      $zip->extractTo('.', 'Fichier.txt');
	
      // Extractions multiples dans le dossier courant.
      $zip->extractTo('.', array('Fichier.txt', 'Autre fichier.txt')); ?>

Autres opérations

La classe ZipArchive possède d’autres méthodes qui permettent de gérer les commentaires sur l’archive et ses fichiers, d’annuler les actions faites depuis l'ouverture, de récupérer les statistiques d’un fichier, etc.

<?php 	// Mettre un commentaire sur l’archive.
	$zip->setArchiveComment('Mon joli commentaire sur l\'archive.');
	
	// Récupérer le commentaire sur l’archive.
	$commentaire = $zip->getArchiveComment();
	
	// Mettre un commentaire sur un fichier.
	$zip->setCommentIndex(3, 'Mon joli commentaire de fichier.');
	$zip->setCommentName('Fichier.extension', 'Et paf un commentaire !');
	
	// Récupérer le commentaire sur le fichier.
	$commentaire = $zip->getCommentIndex(3);
	$commentaire = $zip->getCommentName('Fichier.extension'); ?>

S’il y a un nombre conséquent d’archives *.zip, on peut se servir des commentaires pour préciser leur contenu lorsque celui-ci n’est pas explicité par le nom (il en va de même pour les fichiers si leurs noms ne sont également pas significatifs).

Pour ce qui est de l’annulation de modifications, je vous laisse lire la documentation :

Pour récupérer le contenu d’un fichier présent dans l’archive, on peut récupérer un flux (resource) de la même manière qu’avec fopen() qu’on pourra ensuite exploiter uniquement au moyen de fonctions de lecture comme fgetc(), fgets(), fread(), etc., étant donné que l’écriture n’est pas encore implémentée.
On peut s’en servir pour vérifier le contenu d'un fichier (grâce aux sommes md5 ou sha1, par exemple), récupérer un fichier sans extraire toute l’archive, etc.

<?php $flux = $zip->getStream('Fichier.extension');
      // Je ne teste pas si le flux est bien retourné mais normalement il faut le tester, avec is_resource(), par exemple.
      $temp = '';
      
      while($ligne = fgets($flux))
      {
        $temp .= $ligne;
      }

      echo $temp; ?>

Ci-dessous, un exemple de récupération directe de contenu sans passer par une fonction f*().

<?php	// Récupérer le contenu d’un fichier directement avec l'index du fichier ou son nom.
	$contenu = $zip->getFromIndex(3);
	$contenu = $zip->getFromName('Fichier.extension'); ?>

L’inverse de ZipArchive::locateName() est ZipArchive::getNameIndex(), qui permet d’obtenir le nom d’un fichier à partir de son index.

Il existe bien d’autres fonctions qui sont toutes consultables ici.

T.P. — Récupérer une archive d’un dossier

Sujet

Rien ne vaut un bon T.P. pour finir ce tutoriel assez simple. Pour ceux qui connaissent Dotclear, bien que cela doit exister ailleurs aussi, vous avez pu remarquer la possibilité de « Télécharger ce répertoire dans un fichier zip ». L’objectif de ce T.P. est donc le suivant : récupérer le contenu d’un dossier dans une archive et la proposer en téléchargement.
Je vous renvoie à cette sous-partie du tuto de DHKold. Lisez le passage expliquant comment envoyer un fichier au client dans « Téléchargement avec compteur ».
Voici quelques méthodes pour lister les fichiers dans un dossier :

Vous pouvez ensuite décider d’implémenter ou non la récursivité. Si oui, un dossier présent dans le répertoire à archiver sera lui aussi mis dans le fichier zip. Sinon, il ne sera pas pris en compte et seuls les fichiers se trouvant directement dans le répertoire courant seront pris en compte.

Après, tout dépend de vous, le corrigé vous présentera une méthode sans la récursivité, pour faciliter les choses.

Attention ! 3, 2, 1, Codez ! Oui, je m’y crois trop. :p

Solution

Ma solution n’est pas la solution, c’est simplement une solution possible.

<?php // On instancie la classe.
      $zip = new ZipArchive();
      
      if(is_dir('upload/'))
      {
        // On teste si le dossier existe, car sans ça le script risque de provoquer des erreurs.
	
        if($zip->open('Archive.zip', ZipArchive::CREATE) == TRUE)
	{
	  // Ouverture de l’archive réussie.

	  // Récupération des fichiers.
	  $fichiers = scandir('upload/');
	  // On enlève . et .. qui représentent le dossier courant et le dossier parent.
	  unset($fichiers[0], $fichiers[1]);
	  
	  foreach($fichiers as $f)
	  {
	    // On ajoute chaque fichier à l’archive en spécifiant l’argument optionnel.
	    // Pour ne pas créer de dossier dans l’archive.
	    if(!$zip->addFile('upload/'.$f, $f))
	    {
	      echo 'Impossible d&#039;ajouter &quot;'.$f.'&quot;.<br/>';
	    }
	  }
	
	  // On ferme l’archive.
	  $zip->close();
	
	  // On peut ensuite, comme dans le tuto de DHKold, proposer le téléchargement.
	  header('Content-Transfer-Encoding: binary'); //Transfert en binaire (fichier).
	  header('Content-Disposition: attachment; filename="Archive.zip"'); //Nom du fichier.
	  header('Content-Length: '.filesize('Archive.zip')); //Taille du fichier.
	  
	  readfile('Archive.zip');
	}
	else
	{
	  // Erreur lors de l’ouverture.
	  // On peut ajouter du code ici pour gérer les différentes erreurs.
	  echo 'Erreur, impossible de créer l&#039;archive.';
	}
      }
      else
      {
        // Possibilité de créer le dossier avec mkdir().
        echo 'Le dossier &quot;upload/&quot; n&#039;existe pas.';
      } ?>

Voilà, ce tutoriel est fini.
J’espère vous avoir aidés. Si vous avez une question vous pouvez me contacter par M.P. :)

Exemple de certificat de réussite
Exemple de certificat de réussite