Das Problem
An unserer Schule wird Moodle als LMS (Learning Management System) verwendet. Es eignet sich sehr gut um Unterrichtsunterlagen wie Skripts, Aufträge und Präsentationen zu publizieren und den Lernenden auf eine angenehme Art und Weise zur Verfügung zu stellen. In einigen Kursen gibt es sehr viele dieser Ressourcen (Dateien und Verzeichnisse) zum Download und alleine schon das Anklicken und Speichern nimmt einen grossen Teil des zur Verfügung stehenden Zeitbudgets ein.
Einzelne Ressourcen können von der Lehrperson unsichtbar gemacht werden und nicht alle Dateien eines Kurses stehen zum Download bereit. Die Ansicht ist also sehr individuell und kann sich schnell ändern. Die Dateien werden über eine Versionsverwaltung in das System eingespiesen und können sich so ausserhalb des Systems verändern. Leider verunmöglicht dies die offensichtliche Lösung, einfach ein statisches Zip-Archiv zum Download zur Verfügung zu stellen.
Die Lösung
Das Zip muss also dynamisch (im Speicher) generiert werden bei einem Aufruf. Fündig wurde ich bei der Klasse archive bei phpclasses.org. Neben Zip-Archiven kann sie auch tar’s, gzip’s und bzip2’s erzeugen. Ich habe einige, kleine Erweiterungen vorgenommen, die noch beschrieben werden.
Die Handhabung ist sehr einfach:
require_once ('lib/archive.php');
// Objekt erzeugen. Das Argument bezeichnet den Dateinamen
$zipfile= new zip_file('Meine Zipdatei.zip');
// Die Optionen
$zipfile->set_options(array (
'basedir' => "/home/me/toZip/", // Das Basisverzeichnis. Sonst wird der ganze Pfad von / an im Zip gespeichert.
'followlinks' => 1, // Symlinks sollen berücksichtigt werden
'inmemory' => 1, // Die Datei nur im Speicher erstellen
'level' => 6, // Level 1 = schnell, Level 9 = gut
'recurse' => 1, // In Unterverzeichnisse wechseln
// Wenn zu grosse dateien verarbeitet werden, kannes zu einem php memory error kommen
// Man sollte nicht über das halbe memory_limit (php.ini) hinausgehen
'maxsize' => 12*1024*1024 // Nur Dateien die <= 12 MB gross sind zippen
));
// Alle Dateien im Verzeichnis /home/me/toZip/Stuff hinzufügen
// Alle ".doc" Dateien und alle Ordner im Verzeichnis /home/me/toZip/Letters hinzufügen
$zipfile->add_files(array("Stuff", "Letters/*.doc"));
// Alle ".tmp" dateien in Stuff ausschliessen
$zipfile->exclude_files("htdocs/*.tmp");
// Alle Dateien in ".svn" und "CVS" Verzeichnissen ausschliessen (Regular Expressions)
$zipfile->exclude_regexp_files('.*/CVS|.*/CVS/.*|.*/\.svn|.*/\.svn.*');
// Archiv erstellen
$zipfile->create_archive();
// Archiv zum Download anbieten
$zipfile->download_file();
// Oder speichern (vielen Dank an PHPler!!!)
$zipfile->save_file('myzip.zip', $path = '/tmp/');
Änderungen
Ich habe folgende Dinge gegenüber der original archive.php geändert:
- Mittels exclude_regexp_files() können Dateien auf Grund von Regular Expressions ausgeschlossen werden.
- Die maxsize Option erlaubt das Ausschliessen von Dateien die grösser sind als die angegebene Zahl in Byte.
- Verzeichnisse unter Linux werden als Verzeichnisse behandelt (Bug?)
- exclude_file() prüft, ob eine Datei ausgeschlossen würde
Dank PHPler lönnen nun auch mit der Methode save_file($filename, $path = '') inmemory zips als Dateien gespeichert werden.
Meine Version kann natürlich auch frei heruntergeladen werden.
Hallo,
habe das mal ausprobiert, bekomme aber kein Ergebnis. Wie genau muss ich das jetzt machen, wenn ich zum Beispiel den Ordner uploads/images komplett zippen will?
Welche Dateien muss ich erstellen und mit welchen Scriptinhalt???
Also ich habe bislang folgendes versucht:
-Ordner lib erstellt und Datei archieves.php reingeladen
-im basisverz. eine Datei zip.php mit dem obigen Script und meinen Einstellungen erstellt.
Beim Aufruf ist wie gesasgt nichts passiert.
Ich habe Dir gemailt und werde das Problem und die Lösung hier gerne posten…
Vielen Dank für die schnelle Reaktion. Freu mich schon auf die Lösung.
Oky, ich hoffe es hat funktioniert… Der Code in diesem Beitrag hatte Bugs (korrigiert) und es sollten keine nichtexistenten Verzeichnisse ausgeschlossen werden….
Jetzt bleibt nur noch das Problem, dass der Microsoft IE es nicht schafft, den richtigen Dateinamen bzw. -pfad mitzubekommen und immer versucht die PHP-Datei herunterzuladen, anstatt der ZIP-Datei.
hallo,
obwohl der artikel schon etwas älter ist hätte ich da noch ne frage.
und zwar bringt er bei mir immer:
Warning: Cannot modify header information – headers already sent by…
an was könnte das denn liegen? an php 5? (ist mit zip-enabled compiliert.)
ich hab auch keine ausgabe davor gemacht. nur comments im code, wie in deinem beispiel.
hast du da ne idee?
Kann man mit der Class auch ein ZIP-Archiv in ein Verzeichnis entpacken??
Wie müsste der Code dafür aussehen??
Mfg
Hallo
Ich habe die Klasse implementiert und es scheint soweit zu funktionieren. Das Problem ist nur, dass sich die Files im ZIP nicht öffnen lassen.
Beim Öffnen des ZIP Files melden die ZIP Programme folgendes:
WinRAR: „Unexpected end of archive“
Microsoft ZIP: „Der ZIP-komprimierte Ordner ist ungültig oder beschädigt.
Beim Versuch, ein jpg aus WinRar zu öffnen meldet dieser:
Unexpected end of archive
C:\TEMP\test\Meine Zipdatei.zip: Either multipart or corrupt ZIP archive
An was könnte das liegen? Serverkonfiguration? Zur Info: Damit ich die Header Funktion innerhalb einer Klasse benutzen kann, habe ich in .htaccess php_flag output_buffering On.
Für mögliche Hilfestellungen bin ich euch dankbar.
Was diese Klasse angeht habe ich noch einige Anfragen offen. Das Problem ist halt, ich komm so selten an Windows Compies :-(… Aber ich hab Dir gemailt…
Vielen Dank für dein Script,
ich wollte noch gerne erfahren, ob es möglich ist,
Dateien zu zippen – ohne die Ordnerstruktur einzubeziehen.
z.B.
$zipfile->add_files(array(„ordner/ordner/*.jpg“));
…
und das zipfile soll jedoch nur die files beinhalte, nicht die ordner (-struktur).
bitte hinterlass mir mal eine zeile,
lg,
marc
Das ist natürlich etwas tricky wegen doppelten Dateinamen. Laut http://www.phpclasses.org/browse/file/6584.html müsste das gehen, wenn Du bei den Optionen
auf
setzt. In unserem Beispiel also:
'basedir' => "/home/me/toZip/", // Das Basisverzeichnis. Sonst wird der ganze Pfad von / an im Zip gespeichert.
'followlinks' => 1, // Symlinks sollen berücksichtigt werden
'inmemory' => 1, // Die Datei nur im Speicher erstellen
'level' => 6, // Level 1 = schnell, Level 9 = gut
'recurse' => 1, // In Unterverzeichnisse wechseln
'storepath' => 0, // Unterverzeichnisse nicht speichern
// Wenn zu grosse dateien verarbeitet werden, kannes zu einem php memory error kommen
// Man sollte nicht über das halbe memory_limit (php.ini) hinausgehen
'maxsize' => 12*1024*1024 // Nur Dateien die <= 12 MB gross sind zippen
));
ich habs aber nicht getestet.
ich habe das script in meine homepage eingebaut und es funktioniert perfekt, mit nur ein paar kleinen änderungen lassen sich bestimmte dateien (werden aus einer mysql datenbank entnommen) zippen und downloaden.
Es ist ein tollest script, danke.
Soll ich auf der Homepage angeben das es von dir ist oder war es einfach nur so ein „spasprojekt“? 😛
danke
Vielen Dank für die Blumen! Es freut mich sehr wenn ich Rückmeldungen erhalte.
Ebenfalls ist die Frage nach der Quellenanfrage sehr nett und wirklich höflich. Du musst meinen Namen nicht angeben und kannst es verwenden, wie Du möchtest.
Hallo,
eine coole Klasse hast du da Programmiert. Ich habe mal noch eine Kleinigkeit hinzugefügt:
* this method saves the current archive-stream to the filesystem
* @param string $filename
* @param string $path
* @return int success-/error-code
*/
function save_file($filename, $path = '') {
if ($this->options['inmemory'] == 0) {
$this->error[]= "Can only use save_file() if archive is in memory.";
return;
}
if($fhandle = @fopen($path . $filename, 'w')) {
if(@fwrite($fhandle, $this->archive)) {
if(@fclose($fhandle))
return 1;
else
return 0;
}else{
return -2;
}
}else{
return -1;
}
}
Gruß
PHPler
Heyo, vielen Dank! Das ist eine geniale Erweiterung. Ich habe sie aufgenommen und Dich erwähnt und hoffe, das ist ok so für Dich….
Hi,
Tolles, einfaches script, leicht anpassbar und einsetzbar.
Vielen Dank!
Vielen Dank für das Lob *blush*…
Sensationell.
Nach schier endlosem Suchen endlich mal ein Skript, welches wirklich funktioniert. Du hast mir echt den Tag gerettet 🙂
Es ist ein von wenig Erfolg gekröntes Unterfangen, ein ZIP-Skript zu finden, welches sowohl Linux-Verzeichnisse als solche interpretiert, als auch das Archiv als Datei zwischenspeichert (was bei größeren Datenmengen sehr wichtig ist).
Ich arbeite gerade an einem One-Script-File-Manager und eine Funktion zum Download Verzeichnissen hat mir viele graue Haare beschert^^
Mit dankendem Grinsen,
misterunknown
Freut mich sehr, dass es Dir geholfen hat. Viel Erfolg mit Deinem Script..
Wirklich tolles Skript. Nach längerem Anpassen klappt auch alles. Ich habe nur ein anderes Problem. Bei mir werden leider die deutschen Sonderzeichen nicht richtig angezeigt. Aus „ü“ wird zum Beispiel „³“ usw… Wo kann ich den in der Klasse die entsprechende Formatierung umstellen. Irgendeine Idee?
Danke nochmals.
LG.
Max
super script. Habe es etwas an meine Bedürfnisse angepasst und jetzt funktioniert alles einwandfrei bis auf dasgleiche Problem, dass auch Max angesprochen hat:
Dateien, die Umlaute oder Ähnliches enthalten werden falsch kodiert und in der zip-Datei nicht wie das Original dargestellt. Habe schon im Internet gesucht und eventuell hängt es mit der Kodierung zusammen. Doch meine Änderungen an der Kodierung der archive.php sowie der example.php haben leider nichts geändert.
Muss ich evtl. noch den header-Befehl anpassen?
Habt ihr vielleicht weitere Ideen?
Hast Du das Problem mit den Pfaden, den Dateinamen oder mit Beidem? Unter welchem Betriebssystem? Am Besten könnte ich das debuggen mit einem Codebeispiel…