ZIP-Archive mit PHP unter Unix erstellen

In diesem Artikel wird beschrieben wie man auch ohne PHP Erweiterungen ZIP-Archive unter Unix auf dem Server dynamisch erstellen kann.

In diesem Artikel beschreibe ich wie man auch ohne PHP Erweiterungen ZIP-Archive unter Unix auf dem Server in Echtzeit erstellen kann.

Anatomie eines Archivs

Ein ZIP-Archiv besteht aus zwei großen Teilen: Die gepackten Dateien als Datenstrom und eine Dateitabelle mit Typinformationen.

Im Datenstrom findet man folgende Informationen über die Datei:

  1. CRC32 der Originaldatei,
  2. Länge des komprimierten Dateiinhaltes,
  3. Länge der Originaldatei,
  4. Länge von Dateipfad und –name,
  5. (binäres Trennzeichen),
  6. Dateipfad und –name,
  7. komprimierter Dateiinhalt,
  8. Danach erneut CRC32 der Originaldatei, Länge des komprimierten Dateiinhaltes, Länge der Originaldatei

In der Allokationstabelle findet man folgende Informationen über die Datei:

  1. CRC32 der Originaldatei,
  2. Länge des komprimierten Dateiinhaltes,
  3. Länge der Originaldatei,
  4. Länge von Dateipfad und –name,
  5. (mysteriöse binäre Trennzeichen),
  6. Position des zugehörigen Datenstroms innerhalb des Archivs,
  7. Dateipfad und –name.

Am Ende des Archivs werden noch statische Daten geschrieben: Anzahl der Dateien im Archiv, Länge von Datenblock und Allokationstabelle.

Die binären Trennzeichen im Datenblock und in der Allokationstabelle werden in der Regel durch 16 Bit Null-Blöcke dargestellt (also in hex so: 0x0000).

Komprimierungsverfahren

Einen komplexen Komprimierungsalgorithmus müssen Sie nicht neu implementieren dafür stellt PHP seit Version 4 die Funktion GzCompress() zur Verfügung.

Wie sieht das in PHP aus?

Um das Format darzustellen habe ich hier schon mal eine Klasse für PHP5 vorbereitet:

Class ziparchive {
  var $arrStream = Array();
  var $fat       = Null;
  var $data      = Null;

  function addFile($filename, &$stream){
      $this->arrStream[] = Array ( $filename, &$stream );
   }

  Function Pack(){
    ForEach ($this->arrStream AS $index => &$file){
      $filename = &$file[0];
      $stream = &$file[1];

      $compressedData = SubStr (GZCompress(&$stream, 9), 2, -4);
      $NUL = Pack("v", 0 );

      // File allocation table:
      $this->fat .= "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00".
                     Pack("V", CRC32(&$stream)).
                     Pack("V", StrLen (&$compressedData)).
                     Pack("V", StrLen(&$stream)).
                     Pack("v", StrLen(&$filename)).
                     $NUL.
                     $NUL.
                     $NUL.
                     $NUL.
                     Pack("V", 32 ).
                     Pack("V", StrLen(&$this->data)).
                     $filename;

      // Compressed Data:
      $this->data .= "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00".
                    Pack("V", CRC32(&$stream)).
                    Pack("V", StrLen (&$compressedData)).
                    Pack("V", StrLen(&$stream)).
                    Pack("v", StrLen(&$filename)).
                    $NUL.
                    $filename.
                    $compressedData.
                    Pack("V", CRC32(&$stream)).
                    Pack("V", StrLen (&$compressedData)).
                    Pack("V", StrLen(&$stream));

      // Free The Memory!!!
      $this->arrStream[$index] = Null;

      #Echo "Need memory: ".(memory_get_usage(TRUE) / 1024 / 1024)." M\r\n";
    }

    // Free The Memory!!!
    Unset ($compressedData);

    // Build the binary file:
    return $this->data.
           $this->fat.
           "\x50\x4b\x05\x06\x00\x00\x00\x00".
           Pack("v", Count(&$this->arrStream)).
           Pack("v", Count(&$this->arrStream)).
           Pack("V", StrLen(&$this->fat)).
           Pack("V", StrLen(&$this->data)).
           "\x00\x00";
  }

}

Dokumentation

Die Klasse ist voll funktionsfähig und kann z.B. so benutzt werden:

// Zip Klasse inkludieren
Include "class_zip.php";

// Neues zip-Archiv erstellen
$zip = New ziparchive;

// Dateien hinzufügen
$zip->addFile ( 'class_zip.php', File_Get_Contents ('files/quellcode.php') );
$zip->addFile ( 'sounds/klang.mp3', File_Get_Contents ('files/klang.mp3') );
$zip->addFile ( 'pictures/bild.bmp', File_Get_Contents ('files/bild.bmp') );

// Neues zip-Archiv in Datei schreiben
File_Put_Contents ( "test.zip", $zip->Pack() );

Download

Zip-Klasse für PHP5 inkl. Beispielcodes herunterladen.