PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 30 MB zip lesen


Phate
27.08.2007, 13:04:40
Hallo zusammen,

ich habe ein Problem dessen Lösung sicherlich sehr einfach ist, mir aber nicht klar werden will. Ich stehe vor dem Problem, dass ich Dateien auf einem Webserver einlesen muss und diese dann an den Client liefere. Das geht soweit ja auch ganz gut - readfile() sei dank.

Das ganze geht aber nicht mehr, wenn die zu lesende Datei größere ist als der Ausgabebuffer. Diese ist in meinem Fall mit 25-30 MB beziffert und von mir nicht änderbar. Da ich eine zip-Datei habe, die ~30 MB groß ist, muss ich einen anderen Weg finden diese an den Client zu liefern.

Meine Idee ging in diese Richtung:

$filename_splitted = explode('.', $filename);
$filetype = $filename_splitted[count($filename)];
header('Content-type: application/'.$filetype);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($filepath));
$fp = fopen($filepath, "rb");
if($fp)
{
while($output = fgetc($fp))
{
print($output);
flush();
ob_flush();
}
}
fclose($fp);
exit;

Allerdings erhalte ich hier immer die Fehlermeldung, dass die Datei beschädigt ist. Ersetzte ich fgetc($fp) durch fread($fp, 1024) lädt zwar die Datei aber hat ein Fehlerhaftes Dateiende -> auch kaputt.

Wie bekomme ich denn die komplette Datei. Zeilenweise lesen wird wohl nicht Zielführend sein, denn eine zip-Datei hat schließlich keine Zeilen (oder doch????).

Wäre sehr dankbar für eine Hilfreiche Antwort. Mit den php.net-Texten und Kommentare habe ich keinen Weg gefunden.

Danke!

Markus

rambi
27.08.2007, 13:33:54
while($output = fgetc($fp)) bricht beim ersten auftreten einer binären Null ab. Der Weg tuts nicht!! fread ist da schon günstiger.. du solltest allerdings mit http://de3.php.net/manual/de/function.feof.php prüfen.
Auch magic-quotes-runtime könnte dir dazwischen funken: http://de3.php.net/manual/de/ref.info.php#ini.magic-quotes-runtime

Phate
27.08.2007, 14:26:29
Hallo rambi,
vielen Dank für deine Antwort. Das ich feof nutzen sollte ist mir auch bewusst geworden. Inzwischen hatte ich sogar ein Erfolgserlebnis. Ich hatte dieses hier geschrieben:

$filename_splitted = explode('.', $filename);
$filetype = $filename_splitted[count($filename)];
header('Content-type: application/'.$filetype);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize($filepath) + 1);
$fp = fopen($filepath, "rb");
if($fp)
{
$i = 0;
while(!feof($fp) && connection_status() == 0)
{
print(fread($fp, 1024));
if($i++ % 1024 == 0)
{
ob_flush();
flush();
}
}
}
ob_flush();
flush();
fclose($fp);
exit();

Das ging einwandfrei, zumindest bis ich auf die Idee gekommen bin auch mal die Browserkompatibilität zu testen. Ich hätte es besser gelassen, ich musste ziemlich fluchen. IE7 angewendet und der Script lädt wieder nur einen Teil der Datei. Ich stehe also immernoch vor dem Problem des Lesens. Aber jetzt ist das Problem nicht mehr der Ausgabebuffer sondern mehr der Browser.

Desweiteren musste ich feststellen, dass header('Content-Length: '.filesize($filepath)); nicht funktioniert. Addiere ich aber 1 Byte drauf, geht das Script. Auch eine Sache die mich sehr verwirrt.

Die Problematik, die im Zusammenhang zu magic-quotes-runtime steht habe ich leider nicht verstanden.

Gruß
Markus

rambi
27.08.2007, 14:35:32
Erstmal dürfte dein $filetype immer leer bleiben.
Und die ganzen ob_* und flush() sind wohl auch nicht wichtig...

Was verstehst du an den magic-quotes-runtime nicht? Wenn eingeschaltet, dann streut es dir Backslashes in die Daten! Willst du das ?

meikel (†)
27.08.2007, 16:28:06
Willst du das ?
"... wenns scheen macht...?"

Phate
28.08.2007, 10:06:40
Naja schön ist was anderes :D

Hatte die angewendet um den Buffer weitestgehend sauber zu halten (Den Müll den man macht, sollten man auch wieder wegwerfen) aber wie dem auch sei, stören tut es ja nicht. Mit dem filtyp, ups. Jau das konnte nix werden. Habe ich gefixed - hatte aber wie zu erwarten keine Auswirkung auf die Funktionalität meines Scripts.

Es gilt weiterhin, der Script läuft unter Firefox 2.0.0.6 - nicht aber unter IE 7. Andere habe ich bis dato nicht getestet da es ja schon beim IE 7 nicht geht.

Ich weiß nur nicht woran es hapert. Entweder stehe ich einfach nur wie Ochs vorm Walde oder irgendwas ist grober Mist im Sourcecode.

Des Weiteren ist mir immernoch klar, warum mit filesize() das Zip (im FF) fehlerhaft ist, wenn ich aber + 1 rechne das Zip in Ordnung ist.

Wäre sehr dankbar für Hilfe. Wichtigstes ist erstmal die Funktionalität Browserübergreifend. Diese kleinen Nebenfragen können mir auch egal sein wenn das ganze funktioniert.

Gruß und Danke
Markus

meikel (†)
28.08.2007, 12:43:18
Was steht in $filepath und $filename drin?

Phate
28.08.2007, 13:33:46
$filename ist ein übergebener Wert für den Dateinamen+Dateiendung. $filepath ist ein String der eine relative Ordnerstruktur sowie die Datei enthält.

$filename = $_GET["file"];
$filepath = RelativePath.'/install/daten/'.$filename; // RelativePath = "."
if($filename != "" && file_exists($filepath))
{
// Hier folgt der bekannte Code

Gruß
Markus

meikel (†)
28.08.2007, 15:30:36
nächste Frage:
stimmt die Ausgabe von
echo filesize($filepath);
auch mit der tatsächlichen Dateigröße überein?

Phate
29.08.2007, 13:29:08
Hallo,
also ich habe die Datei mit dem Ergebnis von filesize() verglichen. Das stimmt auf das Byte genau. Aber ich habe etwas anderes festgestellt. Wenn ich die zip-Datei herunter lade, dann bricht er ab. Lade ich allerdings eine ähnlich große (etwas größer) Datei herunter, so funktioniert das einwandfrei. Mit ist der Gedanken gekommen, dass es eventuell etwas mit dem Inhalt der Datei zu tun hat. Das feof überprfüt ja ob das Dateiendezeichen gelesen wurde (Vermutung!!!) und wenn sie den Inhalt der Datei erhält dabei ein Dateiendezeichen findet und eben dieses als Ende der Datei annimmt. Könnte das sein?

Phate
30.08.2007, 12:30:44
Ich habe gestern Abend urplötzlich ein augenscheinlich funktionierende Version gemacht. Und zwar habe ich einfach die folgenden drei header() entfernt. Und oh wunder, es ging.

header('Content-type: application/'.$filetype);
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($filepath));

FF2, IE7, IE6 und Opera luden die Datei vollständig fehlerfrei runter. Lediglich Safari machte mucken -> gab den Inhalt der Datei als Text aus. Aber das ist mir zumindest für den Anfang egal.

Immer noch ist mir aber der Grund für dieses Problem nicht klar.

Es scheint ein Mysterium zu sein, oder jemand ne Idee?

MfG
Phate

z0iD
30.08.2007, 12:39:36
Ich habe dieses Thema hier leider nicht sehr aufmerksam Verfolgt, aber ich stelle mal eine wilde Vermutung auf:

Vielleicht waren die Angaben fehlerhaft oder wurden in der Form nicht von dem Browsern akzeptiert (die senden ja jeweils eine Liste von Dokumenttypen die sie verstehen). Vielleicht hat sie die Dateiendung in ein Standardverhalten springen lassen.

Small-Talk
18.09.2007, 01:36:58
Hab mir das grad durchgelesen und auch eine Vermutung dazu:

Die Angabe in header('Content-Length: '.filesize($filepath)); muss richtig sein, sonnst könnte eventuell der Browser auf die Idee kommen nach der angegebenen länge aufzuhören.
Fehler können dabei sehr schnell entstehen, wenn vor oder nach dem <?php ?> noch etwas kommt. Z.B. UTF-8 -BOM in der Qelldatei drin oder ein Zeilenumbruch am Dateiende ( nach dem ?> ). Das macht der vi sehr gerne.
Unter PHP5 kannst du das letzt ?> weglassen, solange es auch das Dateiende ist.

Wenn du diesen header weglässt sollte der browser die komplette Ausgabe laden, aber er kann einen Ladebalken erstellen, weil er ja nicht weiß wie viel noch kommt.