Wednesday, July 18, 2012

How to add a folder to a zip archive with PHP

If you want to use ZipArchive to archive a folder, you will notice that there is no dedicated method to do this. You will need to make a recursive function to do this.

  function addDirectoryToZip(&$zip, $dir) {
    foreach(glob($dir . '/*') as $file) {
      if(is_dir($file))
        addDirectoryToZip($zip, $file);
      else
        $zip->addFile($file);
    }
  }

Now you have the function, but if you want to add to archive a directory path, something like '/path/to/my/directory', you will find that path in the archive too. So, to have a nice archive, only with tha one directory you need to archive, we will rewrite the function:

  function addDirectoryToZip(&$zip, $dir, $base = 0) {
    foreach(glob($dir . '/*') as $file) {
      if(is_dir($file))
        addDirectoryToZip($zip, $file, $base);
      else
        $zip->addFile($file, substr($file, $base));
    }
  }

Now, the function substracts the base directory from the local name of the file, the name in the zip archive.

Assuming that you have a folder '/path/to/my/folder', you will call the function like this:

  $zip = new ZipArchive;
  $zip->open('ziparchive.zip', ZipArchive::CREATE);
  
  addDirectoryToZip(
    $zip, // the ZipArchive object sent by refference
    '/path/to/my/folder', // The path to the folder you wish to archive
    strlen('/path/to/my/folder') + 1 // The string length of the base folder
  );

To make this even easier, you can extend the ZipArchive class and insert the function into the class:

  class MyZipArchive extends ZipArchive{
    public function addDirectory($dir, $base = 0) {
      foreach(glob($dir . '/*') as $file) {
        if(is_dir($file))
          $this->addDirectory($file, $base);
        else
          $this->addFile($file, substr($file, $base));
      }
    }
  }
  
  // class usage

  $zip = new ZipArchive;
  $zip->open('ziparchive.zip', ZipArchive::CREATE);
  $zip->addDirectory(
    '/path/to/my/folder', // The path to the folder you wish to archive
    strlen('/path/to/my/folder') + 1 // The string length of the base folder
  );

Have fun!

4 comments:

  1. // class usage
    $zip = new MyZipArchive;

    #fixed ;)

    ReplyDelete
  2. Easy, simple, efficient ! Fucking great then !

    ReplyDelete
  3. THank you very very much..I've spend 2 hours on ziparchive::addglob !

    ReplyDelete
  4. This doesn't seem quite right It adds the files from the directory, but it doesn't create the actual directory first. Drop the second argument on the $zip->addFile($file, substr($file, $base)); to just be $zip->addFile($file); and now you are good to go. Then you can add multiple directories and keep the file structure

    ReplyDelete