Idiotensicherer, browserübergreifender Force-Download in PHP


2 Answers

Okay, das ist eine alte Frage und Adam hat bereits seine eigene Antwort akzeptiert. Vermutlich hat er das für sich selbst arbeiten lassen, aber er hat nicht erklärt, warum es funktioniert. Eine Sache, die ich bemerkte, war in der Frage, dass er die Header verwendete:

header("Pragma: public");
header("Cache-Control: public",FALSE);

Während in der Lösung er verwendet:

header("Cache-control: private");
header('Pragma: private');

Er hat nicht erklärt, warum er diese geändert hat, aber ich vermute, dass es sich um die Verwendung von SSL handelt. Ich habe kürzlich ein ähnliches Problem in der Software gelöst, das den Download sowohl über HTTP als auch über HTTPS ermöglichen muss, indem ich den folgenden Header hinzufüge:

if(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) {
    header("Cache-control: private");
    header('Pragma: private');
} else {
    header('Pragma: public');
}

Hoffentlich wird jemand die Informationen in dieser Antwort als nützliche Ergänzung zu den oben genannten finden.

Question

Ich benutze einen erzwungenen Download, um hauptsächlich Zips und MP3s vor Ort herunterzuladen, die ich gemacht habe ( http://pr1pad.kissyour.net ) - um Downloads in Google Analytics zu verfolgen, in der Datenbank und den echten Downloadpfad zu verstecken:

Es ist das:

extending CI model

... - bunch of code

function _fullread ($sd, $len) {
 $ret = '';
 $read = 0;
 while ($read < $len && ($buf = fread($sd, $len - $read))) {
  $read += strlen($buf);
  $ret .= $buf;
 }
 return $ret;
}

function download(){    
    /* DOWNLOAD ITSELF */

    ini_set('memory_limit', '160M');
    apache_setenv('no-gzip', '1');
    ob_end_flush();

    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: public",FALSE);
    header("Content-Description: File Transfer");
    header("Content-type: application/octet-stream");
     if (isset($_SERVER['HTTP_USER_AGENT']) && 
      (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false))
      header('Content-Type: application/force-download'); //IE HEADER
    header("Accept-Ranges: bytes");
    header("Content-Disposition: attachment; filename=\"" . basename("dir-with-    files/".$filename) . "\";");
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: " . filesize("dir-with-files/".$filename));

    // Send file for download
    if ($stream = fopen("dir-with-files/$filename", 'rb')){
     while(!feof($stream) && connection_status() == 0){
      //reset time limit for big files
      set_time_limit(0);
      print($this->_fullread($stream,1024*16));
      flush();
     }
     fclose($stream);
    }
}

Es ist auf LAMP mit CI 1.7.2 - Es ist meine eigene Methode, die aus verschiedenen How-tos über das Internet zusammengestellt wurde, da während der Entwicklung diese Probleme auftraten: - Server-Limit . ini_set hat nicht geholfen, also habe ich gepuffertes _fullread statt normales fread , das nicht @readonly - ob_end_flush () benutzt wurde, da site in CI1.7.2 gemacht wurde und ich den Puffer putzen musste

Jetzt ... Es funktioniert nicht. Es tat, dann hörte es auf, die erwartete Größe / Downloadzeit zu zeigen - ich versuchte es aufzuräumen und während ich den Code aufräumte, passierte etwas, ich weiß nicht was und in irgendeiner früheren Version - es hat nicht funktioniert (nein Änderung der Einstellungen auch immer) - edit : do not work = gibt alles im Browserfenster aus.

Also sagte ich, scheiß drauf, ich schaue hier nach.

Also, ich suche im Grunde nach Skript oder Funktion, die ich in mein Ausgabemodell setzen kann und tun werde:

  • Call Force-Download (in Chrome Start Download, in IE, FF, Safari öffnen Sie das Modal öffnen / speichern / abbrechen)
  • Zeige Größe der Datei und geschätzte DL-Zeit (das hängt vom Browser ab, ich weiß, aber zuerst muss der Browser die Dateigröße kennen
  • ARBEITEN (getestet & bestätigt!) In IE6,7,8, FF3, Opera, Chrome & und Safari auf PC + Mac (Linux ... ist mir egal) - das ist für Header-Teil
  • Auf dem Server habe ich auch etwas wie 56MB Speicherlimit, das ich nicht hinzufügen kann, also ist das auch wichtig

Vielen Dank im Voraus.

Edit : Jetzt fühle ich mich mehr denn je gevögelt, da ich versucht habe, den Download mit .htaccess zu erzwingen - während es funktionierte, hatte es einige kleinere / größere Probleme

  • es zeigte vollen Weg (minderwertig für mich)
  • es wartet bis der ganze Download fertig ist (wird als "connecting" angezeigt) und zeigt dann einfach das Herunterladen - und Downloads in einer Sekunde (wichtig für mich)

Nun, obwohl ich .htaccess gelöscht habe, wartet es immer noch, bis der Download abgeschlossen ist (so als würde es zuerst in den Cache geladen werden) und es wird einfach connected und zeigt den Dialog zum Öffnen / Speichern.




Wenn Sie diese Art von "Echo it out mit PHP" -Methode machen, dann können Sie Ihren Benutzern keine verbleibende Zeit oder eine erwartete Größe anzeigen. Warum? Denn wenn der Browser versucht, den Download in der Mitte fortzusetzen, haben Sie keine Möglichkeit, diesen Fall in PHP zu behandeln.

Wenn Sie einen normalen Datei-Download haben, ist Apache in der Lage, wiederaufgenommene Downloads über HTTP zu unterstützen, aber im Falle eines Downloads hat Apache keine Möglichkeit herauszufinden, wo in Ihrem Skript Dinge ausgeführt wurden, wenn ein Client nach dem nächsten Chunk fragt.

Wenn ein Browser einen Download pausiert, beendet er im Wesentlichen die Verbindung zum Webserver vollständig. Wenn Sie den Download fortsetzen, wird die Verbindung erneut geöffnet, und die Anforderung enthält ein Flag mit dem Hinweis "Start from byte number X". Aber für den Webserver, der oben auf Ihr PHP schaut, woher kommt Byte X?

Während es theoretisch möglich ist, dass der Server im Falle eines unterbrochenen Downloads erkennt, wo das Skript fortgesetzt werden kann, versucht Apache nicht herauszufinden, wo es fortgesetzt werden soll. Daher gibt der Header, der an den Browser gesendet wird, an, dass der Server resume nicht unterstützt, wodurch die erwarteten Größen für Dateigröße und Zeitlimit in den meisten gängigen Browsern deaktiviert werden.

EDIT: Es scheint, dass Sie in der Lage sein, mit diesem Fall umzugehen, aber es wird eine Menge Code auf Ihrer Seite nehmen. Siehe http://www.php.net/manual/en/function.fread.php#84115 .




print($this->_fullread($stream,1024*16));

Ich nehme an, _fullread ist innerhalb einer Klasse? Wenn der Code wie oben $this-> würde $this-> nicht funktionieren.

Gibt es den Dateiinhalt auf dem Bildschirm aus, wenn Sie alle Kopfzeilen auskommentiert haben?




Related