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:
- CRC32 der Originaldatei,
- Länge des komprimierten Dateiinhaltes,
- Länge der Originaldatei,
- Länge von Dateipfad und –name,
- (binäres Trennzeichen),
- Dateipfad und –name,
- komprimierter Dateiinhalt,
- Danach erneut CRC32 der Originaldatei, Länge des komprimierten Dateiinhaltes, Länge der Originaldatei
In der Allokationstabelle findet man folgende Informationen über die Datei:
- CRC32 der Originaldatei,
- Länge des komprimierten Dateiinhaltes,
- Länge der Originaldatei,
- Länge von Dateipfad und –name,
- (mysteriöse binäre Trennzeichen),
- Position des zugehörigen Datenstroms innerhalb des Archivs,
- 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() );