PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Große Dateien komprimiert als Download streamen

with 24 comments

Bei großen Dateien gibt es mehrere Probleme. Sie belegen viel Speicherplatz auf der Festplatte, bei der Komprimierung vergeht viel Zeit, die fertig komprimierte Datei belegt wiederum viel Speicherplatz, und eventuell wird auch viel Arbeitsspeicher benötigt beim Komprimieren oder ausliefern via PHP.

Viele dieser Probleme lassen sich lösen, wie ich gleich zeigen möchte. Wie vieles auf dieser Welt ist das jedoch auch mit kleinen Nachteilen versehen, sodass man abwägen muss, was einem wichtig ist.

Mein kleines Beispiel hier geht davon aus dass es eine oder mehrere große Dateien gibt, die gezippt heruntergeladen werden sollen. Die zu komprimierenden Dateien liegen auf einem anderen Server (hier: Netzlaufwerk), es könnte aber auch ein FTP-Server, ein Upload oder sonst irgendeine Quelle sein. Natürlich funktioniert das ganze auch mit lokalen Dateien oder gerade erst erzeugten Daten (beispielsweise MySQL Dump).

Die erste Lösung wäre diese:

<?php
set_time_limit(0);

copy('\\\\192.168.1.33\\Dateien\\seinfeld.avi', 'seinfeld1.avi');

$zip = new ZipArchive();
$filename = "seinfeld1.zip";

if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
    exit("cannot open <$filename>\n");
}
$zip->addFile('seinfeld1.avi');
$zip->close();

header('Content-type: application/octetstream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
readfile($filename);

Hier haben wir ein 700MB Video als Quelle, das auf einem Netzlaufwerk liegt. Wir kopieren es zuerst auf unsere lokale Festplatte (mit 30MB/s dank Gigabit-Netzwerk) und beginnen dann mit der Komprimierung. Durch die Komprimierung werden temporär weitere 700MB belegt, und danach starten wir direkt mit der Ausgabe der Headern und dem Inhalt der Datei.

Findige Programmierer sehen sofort: Wir können uns das Kopieren der Datei sparen, und die Datei direkt vom Netzlaufwerk an addFile() übergeben. Dadurch sparen wir die 20 Sekunden für die Kopieraktion und beginnen direkt mit der Komprimierung. Das funktioniert auch dank Wrappern mit einigen anderen Protokollen, aber bei einer „unüblichen Quelle“ wie beispielsweise dem MySQL-Dump nicht.

Lösung 2:

<?php
set_time_limit(0);

$zip = new ZipArchive();
$filename = "seinfeld2.zip";

if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
    exit("cannot open <$filename>\n");
}
$zip->addFile('\\\\192.168.1.33\\Dateien\\seinfeld.avi');
$zip->close();

header('Content-type: application/octetstream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
readfile($filename);

Garnicht schlecht, wir haben den Festplattenbedarf halbiert, und auch der Download startet früher. Aber wir haben nach wie vor das Problem dass der Download erst nach 58 Sekunden beginnt, so lange dauert nämlich die Komprimierung. Das ist eigentlich nicht zumutbar. Das wollen wir verbessern.

Wie kann das gehen? Wir komprimieren nicht die ganze Datei und beginnen mit dem Download wenn die ganze Datei komprimiert wurde, sondern wir komprimieren in 100KB Häppchen und senden permanent die Zwischenergebnisse zum Download. Leider hat das ZIP Format den Nachteil dass es direkt am Anfang die CRC-Prüfsumme der Datei wissen will. Das bedeutet wiederum dass wir nicht darum herum kommen, diese anfangs zu berechnen. Dies dauert 20 Sekunden, der Download startet also nach 20 Sekunden. Es werden dann 100KB vom Netzlaufwerk gelesen, komprimiert und an den Browser ausgeliefert. Das geht solange bis die komplette Datei komprimiert wurde.

Da ZipArchive diese Streaming Technik nicht unterstützt muss manuell Hand angelegt werden und die ZIP-Datei erstellt werden. Dazu gehört der Gesamtheader der ZIP Datei sowie zu jeder Datei innerhalb des Archivs die Dateiheader. Das Projekt ZipStream-PHP von Paul Duncan kann das ganz gut. Leider treten bei der Benutzung einige Notices auf, die aber schnell behoben sind, sie treten auch nur auf wenn man nicht alle optionalen Parameter nutzt. Außerdem gibt es ein Problem mit der crc32b Berechnung, die aber auch leicht zu lösen war. Eine funktionierende Version befindet sich hier.

Der Ablauf:

Nutzung von hash_file() um die CRC32-Prüfsumme zu berechnen und den ZIP-Datei-Header zu generieren. Dann werden jeweils 100KB gelesen, komprimiert und ausgegeben. Dies wird wiederholt bis die Datei vollständig komprimiert und ausgegeben wurde.

In der ZipStream Klasse können natürlich noch einige Parameter angepasst werden, beispielsweise die Größe der Päckchen (hier 100KB), man kann aber auch ein Limit festlegen, ab dem dieses Zerhackstückeln passieren soll, unterhalb dieser Grenze wird die Datei als ganze gelesen und komprimiert.

Und hier nun das Script, welches ZipStream nutzt (Lösung 3):

<?php
set_time_limit(0);

require 'zipstream.php';
$zip = new ZipStream('seinfeld3.zip');

$zip->add_file_from_path('seinfeld.avi', '\\\\192.168.1.33\\Dateien\\seinfeld.avi');

$zip->finish();

So einfach kann es sein. Es gibt keine temporäre Datei, der Download beginnt früh, und der Arbeitsspeicher wird auch nicht beansprucht.

Ich habe auch nach einer Lösung gesucht, um „richtiges“ Streaming zu machen und dann mit stream_filter_append() und zlib.deflate zu arbeiten, aber da habe ich nichts gefunden. Vielleicht könnte man ZipStream-PHP so erweitern dass es damit funktioniert.

Aber wie bereits gesagt haben diese Streaming Lösungen auch Nachteile. Ein Nachteil ist beispielsweise die fehlende Möglichkeit, den Download zu pausieren und später fortzufahren (HTTP Ranges). Würde man die Datei nicht streamen sondern wie in den ersten Beispielen erstmal als Datei abspeichern würde das funktionieren, ebenso könnte man dann den eigentlichen Download an einen leichtgewichtigen Webserver wie nginx oder lighttpd übergeben oder mittels Sendfile die Datei vom Apache ausliefern lassen. Dann würde auch HTTP Range unterstützt und mehrere Personen könnten die Datei downloaden. Je nach Anwendungsfall ist also manchmal auch die Nicht-Streaming-Methode besser.

Hier nochmal eine Übersicht der 3 Lösungen:

Lösung 1Lösung 2Lösung 3
Download beginnt nach75 Sekunden58 Sekunden20 Sekunden
Download beendet nach (in Sekunden)141 Sekunden109 Sekunden98 Sekunden
zusätzlicher Festplattenverbrauch1400 MB700 MB0 MB
memory_get_peak_usage(true)500 KB500 KB1 MB

Written by Michael Kliewe

Dezember 2nd, 2010 at 9:10 am

Posted in PHP

Tagged with , , ,

24 Responses to 'Große Dateien komprimiert als Download streamen'

Subscribe to comments with RSS or TrackBack to 'Große Dateien komprimiert als Download streamen'.

  1. […] finde ich nicht schlecht und ist sicherlich eine Lösung große Dateien unkompliziert anzubieten. Große Dateien komprimiert als Download streamen Bei großen Dateien gibt es mehrere Probleme. Sie belegen viel Speicherplatz auf der Festplatte, […]

  2. Mich hätte mal noch interessiert, wieviel Traffic dadurch eingespart wird. Schließlich war das doch auch der initiale Grund warum man eine ZIP Komprimierung möchte, oder? Auch interessant wäre dieses Experiment mit unterschiedlichen Compression-Level zu machen. Und wie hoch ist die zusätzliche CPU Last?

    Gruß Matze

    Semantik Matze

    2 Dez 10 at 13:20

  3. Das kannst du ja zuhause leicht nachprüfen. Das hängt auch absolut von der Art der Dateien ab, Textdateien (oder MySQL Dumps etc) lassen sich natürlich besser komprimieren als bereits komprimierte jpeg Bilder oder divx-Videos.

    Ich benötige die Funktionalität vor allem um mehrere kleine Dateien herunterladen, da möchte ich nicht 10x klicken und einzeln downloaden, sondern nur einen Download (von mir aus auch ein zip ohne Komprimierung) starten und dann lokal das Archiv entpacken.

    Wir reden hier auch nicht von der normalen gzip-Komprimierung von Webseitenelementen (HTML, Bilder, css usw).

    Michael Kliewe

    2 Dez 10 at 15:02

  4. Dein Tweet gestern hat mich sehr neugierig gemacht und ich hab ja schon vermutet das es dann als Blogpost kommt.

    Da ich einen „Package Builder“ habe, war es immer ein Problem, dass die Erstellung so lange gebraucht hat.

    Bin schon gespannt wie es bei mir aussieht.
    Danke

    ragtek

    2 Dez 10 at 15:05

  5. Diese Varianten haben jedoch einige Nachteile in der Praxis. Auch bei 20 Sekunden Wartezeit werden viele Nutzer nach ein paar Sekunden denken, es kommt nichts mehr und laden die Seite neu. Das alte Script läuft im Hintergrund trotzdem weiter und so werden lokal immer mehr Dateien kopiert, der Server wird belastet und Traffic erzeugt.

    Des Weiteren hat man vermutlich eine sinnvolle max_execution_time gesetzt und auf Hosting-Paketen oder Managed Server werden Prozesse sowieso meist nach 90 Sekunden abgeschossen. Selbst wenn das Script schnell arbeitet, hat man keine Kontrolle über die Download-Geschwindigkeit vom Client und ggf. müsste das Script stundenlang laufen. Das funktioniert dann nicht oder – sofern man einen eigenen Server hat und es konfigurieren kann – belastet den Server stark.

    Ein weitere Problem ist das memory_limit, welches mit der ZIP-Klasse vermutlich auch sehr groß gesetzt werden muss.

    Aus diesem Grund bin ich dazu übergegangen, größere Dateien mit system(‚gzip …‘) in ein spezielles Verzeichnis zu komprimieren und dann mit einer Header-Weiterleitung den Client darauf umzubiegen. Dann kann der Webserver die Datei ohne PHP und ohne Limits ausliefern. Bricht der Client mittendrin ab und startet den Download neu, existiert beim nächsten Versuch die Datei schon und der Download kann sofort startet.

    Mit den entsprechenden Einstellungen in der .htaccess (AddEncoding und AddType) werden dann die komprimierten Dateien mit der Endung .gz entsprechend gzip-komprimiert ausgeliefert.

    Vergessen darf man auch nicht, über den Request-Header Accept-Encoding zu testen, ob der Client überhaupt gzip unterstützt. Das ist nämlich lange nicht überall der Fall.

    Mario

    3 Dez 10 at 09:55

  6. @Mario: Ich sehe den Vorteil deiner Vorgehensweise nicht. Deine Argumente:
    – 20 Sekunden Wartezeit. Ja, die hat man, da kann man dann ein „Bitte warten“ einblenden. Das Problem hast du aber noch viel mehr, denn bei dir muss der User 1 Minute warten bis der Download beginnt. Du wirst ihm also auch sowas wie „bitte warten“ anzeigen und dann die Header-Weiterleitung zünden.
    – das alte Script läuft im Hintergrund weiter: Ist das bei einem system() call nicht genauso?
    – max_execution_time: Das ist es bei system() genauso, da es blocking ist. passthru() wäre non-blocking, aber dann weißt du nicht wann der gzip Prozess fertig ist und weiß nicht wann du den Redirect senden sollst. Du gehst auch davon aus dass du system() nutzen kannst, das ist auch nicht in jedem Hosting-Paket verfügbar.
    – „Selbst wenn das Script schnell arbeitet, hat man keine Kontrolle über die Download-Geschwindigkeit vom Client und ggf. müsste das Script stundenlang laufen“ Ich nutze einen nginx, der die eigentlichen Daten an den Browser ausliefert. Sobald das PHP Script fertig ist und alle Daten an nginx übergeben hat, beendet es sich. Ob der nginx dann eine Stunde die Daten langsam ausliefert oder nicht ist mir egal. Das PHP-Script läuft also nur solange es laufen muss, unabhängig vom Client.
    – memory_limit: Wie bereits oben gezeigt braucht das PHP Script nur 1 MB Arbeitsspeicher, das Limit ist kein Problem.
    – In deinem Fall musst du dich drum kümmern die Datei nach dem erfolgreichen Download wieder zu löschen, außerdem benötigst du, wenn du system(‚gzip..‘) verwendest sehr viel Festplattenspeicher (1400 MB).
    – die letzten 2 Absätze verstehe ich nicht. Das Archiv soll als Download angeboten werden, da interessiert der Request-Header Accept-Encoding nicht.

    Es geht hier um den einmaligen Download von komprimierten Daten, der so schnell und effektiv wie möglich erledigt werden soll. Und ich glaube deine Lösung hat mehr Nachteile und keine Vorteile?!

    Michael Kliewe

    3 Dez 10 at 11:05

  7. @Michael: Mein Lösungsansatz ist vielleicht nicht optimal für genau deinen Anwendungsfall zugeschnitten aber trotzdem sind die Hinweise wohl nicht verkehrt. Die meisten werden vermutlich keinen nginx einsetzen und Apache läuft in der Standardkonfiguration bei readfile() so lange weiter, bis die Datei ausgeliefert wurde. Das stößt schon bei kleineren Dateien schnell an die Limits und führt leicht zu unvollständigen Downloads beim Client. Die Erfahrung musst ich auch bei einem Download-Shop und ca. 10 MB großen PDF-Dateien machen.

    Die 20 Sekunden Wartezeit oder länger sind ja nicht wirklich das Problem. Da du aber nichts zwischenspeicherst, brauchst du immer 20 Sekunden, auch wenn der Client (warum auch immer) mehrfach zugreift. Der Speicherplatz auf dem Server ist sicherlich weniger das Problem und die Dateien lassen sich leicht mittels cronjob nach definierten Zeitabschnitten wieder löschen. Bei mir läuft ein Job einmal am Tag und löscht alles was älter als 24 Stunden ist.

    Was den letzten Abschnitt angeht – da hatte ich überlesen, dass du die Dateien sowieso als ZIP-Archiv an den Client übergeben willst. Da ist deine Vorgehensweise natürlich absolut ok. Ich liefere dagegen die Dateien – sofern es der Client unterstützt – GZIP-komprimiert aus, sodass die Übertragung zwar komprimiert ist, die Datei dann jedoch unkomprimiert beim Client gespeichert wird. Der Traffic ist dann gleich, nur muss der Client die Datei nicht noch entpacken.

    Noch zum Abschluss: Je nach Anwendungszweck ist sicherlich die eine oder andere Lösung zu bevorzugen. Deine Variante ist elegant und beim einmaligen Download (für das du es vorgesehen hast) auch die schnellste Lösung. Ich wollte nur auf mögliche Probleme (wie eben beim Apache und dem max_execution_time) hinweisen und eine alternative Lösung für etwas abweichende Einsatzgebiete aufzeigen. Bitte fühle dich da jetzt nicht irgendwie auf dem Schlips getreten – so war es auf jeden Fall nicht gemeint!

    Mario

    3 Dez 10 at 11:48

  8. @Mario: Gut dass ich keinen Schlips habe wo man drauftreten kann 😉

    Beim readfile (bzw. in der 3. Lösung wird ja echo genutzt) bin ich mir nicht ganz sicher ob da der Apache oder das Betriebssystem das Puffern übernehmen, und das PHP Script (bzw. der Interpreter) nicht schon beendet sind und dann das Betriebssystem bzw. Apache noch die Daten ausliefert. Kommt wahrscheinlich auch auf die Größe der Puffer an. Aber das weiß ich grad nicht.

    Ich glaube auch wir gehen von unterschiedlichen Einsatzszenarien aus. Du opferst lieber Speicherplatz und verhinderst dadurch die mehrfache Berechnung, ich wiederum habe nur einmalige Downloads und möchte den Speicherverbrauch klein halten (und damit Disk-IO verhindern).

    Gerade Hosting-Pakete haben häufig nur wenige hundert MB oder wenige Gigabyte Platz. Wenn man also beispielsweise 1GB Speicherplatz besitzt, darin liegen 700MB Dateien, die man als ZIP-Archiv komprimieren und downloaden möchte, werden die ersten beiden Lösungen oben und auch die system(‚gzip‘) Lösung nicht funktionieren, Lösung 3 aber schon. Auch wenn man 5 GB Speicherplatz hätte würde es krachen, wenn nämlich ein User 98 Dateien komprimiert, der nächste 97, dann wieder jemand 96 etc.. Dann sammeln sich schnell viele viele Gigabyte temporäre Dateien an, die der Cronjob 24 Stunden später löscht, bis dahin ist aber schon das Dateisystem vollgelaufen.
    Auch widerstrebt es mir aus I/O Sicht, 700MB auf die Platten zu schreiben nur um sie direkt danach wieder zu lesen, wenn das hunderte Benutzer gleichzeitig tun entsteht ein ekliger Flaschenhals.

    Die gzip-Komprimierung, die du ansprichst, setze ich bei Einzeldateien ein. Dann ziehe ich den „normalen“ Download auch vor und mache garkein Zip-Archiv.

    Aber du hast Recht, die Probleme, die allgemein beim Komprimieren großer Dateien auftreten, hätte ich nennen sollen (vor allem max_execution_time ist ein Problem). In meinem konkreten Fall kann ich die jedoch anpassen.

    Finde ich aber toll dass jemand mitdenkt und nicht alles als perfekt hinnimmt, denn das ist es sicherlich nicht 😉

    Michael Kliewe

    3 Dez 10 at 12:24

  9. Wenn es sich irgendwie vermeiden lässt, dann würde ich definitiv versuchen das anders zu lösen:

    Über den X-Sendfile Header lässt sich das eigentlich mit allen „vernünftigen“ Webservern deutlich geschickter und performanter erledigen:
    http://redmine.lighttpd.net/wiki/1/X-LIGHTTPD-send-file
    https://tn123.org/mod_xsendfile/
    http://wiki.nginx.org/XSendfile

    Das sollte auch aus Performance-Sicht reinen PHP-Lösung deutlich überlegen sein.

    Nitek

    4 Dez 10 at 09:47

  10. @Nitek: Das geht nur mit „statischen“ Dateien, Sendfile könnte man also bei der Lösung 1+2 oder Marios Lösung einsetzen, aber dann hast du eben die Nachteile des spät startenden Downloads und des Festplattenspeicherverbrauchs, den ich ja genau vermeiden möchte. Und beim Streamen mit einem nginx davor übernimmt dieser ja die Auslieferung an den langsamen Client, ich habe also die Vorteile die Sendfile bietet und auch keine so starken IO-Probleme und Festplattenverbrauch, außerdem beginnt der Download deutlich früher.

    Aber Sendfile ist bei bereits existierenden Dateien sehr schön, in diesem Szenario aber nicht einsetzbar (es sei denn ich übersehe etwas).

    Michael Kliewe

    4 Dez 10 at 09:56

  11. Sendfile macht in der Tat nur bei statischen Dateien sind. Für dynamische Dinge muss man sich etwas anderes einfallen lassen, allerdings sollten dynamische Dateien auch in den seltensten Fällen sooo groß sein, dass man sich da überhaupt groß Gedanken drum machen muss.

    Aber Szenario 3 sich mit Sendfile abbilden lässt hängt IMHO davon ab, ob es einem darum geht die Daten lediglich komprimiert zu übertragen oder so wirklich als ZIP-Datei runtergeladen zu bekommen, denn Sendfile lässt sich wunderbar mit der GZIP-Kompression des Webservers kombinieren.

    Nitek

    4 Dez 10 at 10:03

  12. Statt zip schlage ich gzip vor. Damit entfällt die Prüfsumme und der Stream kann unmittelbar starten ..

    Soenke

    5 Dez 10 at 01:01

  13. @Soenke: Ah, interessant! Leider bin ich nicht der einzige Benutzer, ich fürchte Windows kann kein gzip von Haus aus oder?

    Michael Kliewe

    5 Dez 10 at 09:35

  14. AFAIK unterstützt Windows GZIP nicht von Haus aus. Allerdings fällt mir gerade ein, dass du evtl. ein Zwischending von meiner Variante mit Cachen und deiner machen könntest. Wenn du einfach nur den Hash der Datei zwischenspeicherst, dann kann dein Download spätestens beim zweiten Download sofort ohne 20 Sekunden Wartezeit starten.

    Noch besser wäre es, wenn du gleich zu jeder Datei auf dem Server den Hash mit angibst und in deinem Beispiel eine Datei seinfeld.avi.md5 mit anlegst. Dann erweiterst du ZipStream nur noch darum, erst zu schauen, ob es eine Hash-Datei $path.‘.md5′ gibt. Wenn ja, wird der Inhalt geladen und wenn nicht, läuft alles wie gehabt (evtl. dann noch den ermittelten Hash lokal speichern und das noch testen). Zur Sicherheit könnte man in der Datei ja auch Größe und Hash speichern.

    Würde ich jetzt als optimale Lösung empfinden.

    Mario

    5 Dez 10 at 10:20

  15. @Mario: Keine schlechte Idee! Aber in meinem speziellen Fall geht das nicht, da die Daten nicht als Dateien vorliegen, sondern erst generiert bzw. von einem Dienst abgerufen werden.
    Aber für einige andere Einsatzzwecke, wo Dateien mehrfach heruntergeladen werden ist das durchaus sinnvoll! Man muss natürlich darauf achten dass niemand die Dateien verändern kann. Wenn man also gleicheitig via FTP, Samba, WebDav und Web-Filemanager zugreifen kann muss man extrem aufpassen beim Überschreiben von Dateien, oder auch gleich die .md5 Datei bzw. den Datenbankeintrag löschen bzw. updaten.

    Michael Kliewe

    5 Dez 10 at 10:33

  16. Ok, dann geht das nicht. Eine Frage aber noch: ich habe mir ZipStream mal kurz angeschaut und wenn ich das richtig sehe, wird (zumindest in deinem Beispiel) die Datei doch gar nicht komprimiert?! Ist da nicht opt[‚large_file_method‘] == ’store‘ und wird damit nicht die Datei direkt, ohne gzdeflate() (Zeile 378), an den Client ausgegeben (Zeile 382)?

    Wenn ich da jetzt nicht komplett falsch liege, dann wäre in dem Fall ZIP ja sogar unnötig, da eine unkomprimierte ZIP-Datei ja nichts bringt und man dann gleich die Originaldatei schicken kann und sich den Hash spart.

    Mario

    5 Dez 10 at 11:08

  17. Oha, die Zeile hab ich ja garnicht gesehen, ich glaube du hast Recht!

    Möchte man das ZIP-Archiv also auch komprimieren muss man es auf „deflate“ umstellen, dann erhöht sich jedoch der RAM-Verbrauch und es wird langsamer (steht oben in einem Kommentar).

    Wenn man die store-Methode benutzt um aus vielen Dateien eine zu machen sollte man also darauf achten dass der Download dann via Browser-gzip-Komprimierung geschieht (Accept-Encoding). Dann hat man eine schnelle Datenübertragung und den Vorteil, nur eine Datei herunterladen zu müssen.

    Den Hash spart man sich übrigens nicht, siehe Zeile 340.

    Ich hatte „deflate“ kurz ausprobiert und gesehen dass es deutlich langsamer ist, aber keine Messwerte erhoben. Das sollte ich nachholen.

    Oha, das ändert einiges, Danke für den Hinweis!

    Michael Kliewe

    5 Dez 10 at 11:24

  18. Mit „Hash sparen“ meine ich nur, dass man – sofern es sich nur um eine einzelne Datei handelt – dann gar kein ZIP braucht und man die Datei gleich unkomprimiert an den Client schicken kann. Da bei der Methode ’store‘ ja keine Komprimierung stattfindet, wird der Download nicht kleiner (durch den Header sogar etwas größer). Ohne ZIP muss man dann auch keinen Hash ermitteln und kann die Datei direkt schicken (unter Beachtung von Accept-Encoding ggf. mit gzip).

    Bei mehreren Dateien sieht es dann natürlich schon anders aus. Dort kommt man um einen Container wie ZIP nicht herum und braucht auch einen Hash.

    Mario

    5 Dez 10 at 11:38

  19. Das stimmt, das mache ich auch so, Einzeldateien werden einfach so zum Download angeboten. Ich benötige, wie du bereits sagst, einen Container für mehrere Dateien, den jedes moderne Betriebssystem unterstützt, und da kommt man an ZIP eigentlich nicht vorbei. Für Linux+Mac hätte man auch tar, tar.gz etc nehmen können, leider ist die Welt nicht so einfach dank Windows 😉

    Michael Kliewe

    5 Dez 10 at 11:50

  20. […] PHP: Große Dateien komprimiert als Download streamen. […]

  21. Das passt perfekt. Ich war heute gerade am recherchieren in dieser Richtung für ein paar Denkanstöße 🙂

    Michael

    9 Dez 10 at 21:04

  22. […] Graph API Thermometer-Pflaster macht Patientenstirn zur Infokonsole – Engadget German Große Dateien komprimiert als Download streamen | PHP Gangsta – Der PHP Blog TabCloud – Chrome Session Manager mit integrierter Online Synchronisation > Chrome > Addon, […]

  23. Hey ihr zwei,
    Habe mit Spannung den Blogpost und eure Kommentare verfolgt. Werde das ganze mal für mich testen, da Lösung 3 für meine Bedürfnisse genau das Richtige zu sein scheint. Danke 🙂

    Kai

    31 Mrz 16 at 11:58

  24. Moin, bin zufällig auf diesen (älteren ;-))) Beitrag gestossen. Lösung 3 hat mich bei einem Problem gerettet. Klappt auch mit PHP 7.4 ohne Anpassung…
    Herzlichen Dank.

    Dirk

    24 Sep 21 at 21:30

Leave a Reply

You can add images to your comment by clicking here.