So erzwingen Sie das Herunterladen von Dateien mit PHP

122

Ich möchte, dass eine Datei heruntergeladen wird, wenn der Benutzer eine Webseite mit PHP besucht. Ich denke, es hat etwas damit zu tun file_get_contents, bin mir aber nicht sicher, wie ich es ausführen soll.

$url = "http://example.com/go.exe";

Nach dem Herunterladen wird eine Datei header(location)damit nicht auf eine andere Seite umgeleitet. Es hört einfach auf.

John
quelle
Mögliches Duplikat des Erzwingens des Herunterladens einer Seite in PHP
Hakre
Mögliches Duplikat von Forcen zum Herunterladen einer Datei mit PHP
Benutzer

Antworten:

232

Lesen Sie die Dokumente zur integrierten Readfile der PHP-Funktion

$file_url = 'http://www.myremoteserver.com/file.exe';
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary"); 
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\""); 
readfile($file_url); 

Stellen Sie außerdem sicher, dass Sie den richtigen Inhaltstyp basierend auf Ihrer Datei application / zip, application / pdf usw. hinzufügen - jedoch nur, wenn Sie den Dialog zum Speichern unter nicht auslösen möchten.

Pit Digger
quelle
6
Warum ist das erforderlich?
John
Ich meine, wenn es eine andere als .exe-Datei als nur .Else ist, sollte dies gut funktionieren.
Pit Digger
7
Vergiss nicht zu spülen ;-) ob_clean (); spülen(); / * vor * / readfile ($ file_url);
Ash
Also, wenn Sie eine 10 GB große Datei haben, versucht PHP, diese ganze Datei zu laden?
GDY
exit () sollte am Ende aufgerufen werden, um mögliche Probleme zu vermeiden (aus Erfahrung :-)
ykay sagt Reinstate Monica
42
<?php
$file = "http://example.com/go.exe"; 

header("Content-Description: File Transfer"); 
header("Content-Type: application/octet-stream"); 
header("Content-Disposition: attachment; filename=\"". basename($file) ."\""); 

readfile ($file);
exit(); 
?>

Wenn die Datei nicht mit dem Browser geöffnet werden kann, können Sie einfach den LocationHeader verwenden:

<?php header("Location: http://example.com/go.exe"); ?>
Marek Sebera
quelle
1
Der Dateiname in Ihrem Header sollte nicht $file(der den http-Teil enthält) sein, sondern ein gültiger Dateiname.
Fabio
1
Es gibt keinen Medientyp für Anwendung / Force-Download . Verwenden Sie stattdessen application / octet-stream .
Gumbo
funktioniert sehr schön! Fügt aber 'zum gespeicherten Dateinamen hinzu. Bitte verwenden Sie: filename = ". Basename ($ file));
harry4516
@ harry4516 geändert nach Ihrem Befund
Marek Sebera
Funktioniert nicht lieb im Falle eines PNG-Bildes für mich. Vielen Dank.
Kamlesh
40
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\""); 
echo readfile($url);

ist richtig

oder besser für exe- Dateityp

header("Location: $url");
Genesis
quelle
@Fabio: Wird weitere Detials hinzufügen
Genesis
Das war super einfach. Es funktionierte. Wofür ist file-Get_contents dann? Nur neugierig. Vielen Dank.
John
um es auf Ihren Server herunterzuladen
Genesis
10
Entfernen Sie einfach 'echo' vor 'readfile ()', da der Rückgabewert als "Gibt die Anzahl der aus der Datei gelesenen Bytes zurück. Wenn ein Fehler auftritt, wird FALSE zurückgegeben und es sei denn, die Funktion wurde als @readfile () aufgerufen. Eine Fehlermeldung wird gedruckt. ". So erhalten Sie den Inhalt der Datei + Ganzzahl am Ende des Inhalts.
Mladen B.
22

Zeigen Sie Ihre Datei zuerst an und setzen Sie ihren Wert in url.

index.php

<a href="download.php?download='.$row['file'].'" title="Download File">

download.php

<?php
/*db connectors*/
include('dbconfig.php');

/*function to set your files*/
function output_file($file, $name, $mime_type='')
{
    if(!is_readable($file)) die('File not found or inaccessible!');
    $size = filesize($file);
    $name = rawurldecode($name);
    $known_mime_types=array(
        "htm" => "text/html",
        "exe" => "application/octet-stream",
        "zip" => "application/zip",
        "doc" => "application/msword",
        "jpg" => "image/jpg",
        "php" => "text/plain",
        "xls" => "application/vnd.ms-excel",
        "ppt" => "application/vnd.ms-powerpoint",
        "gif" => "image/gif",
        "pdf" => "application/pdf",
        "txt" => "text/plain",
        "html"=> "text/html",
        "png" => "image/png",
        "jpeg"=> "image/jpg"
    );

    if($mime_type==''){
        $file_extension = strtolower(substr(strrchr($file,"."),1));
        if(array_key_exists($file_extension, $known_mime_types)){
            $mime_type=$known_mime_types[$file_extension];
        } else {
            $mime_type="application/force-download";
        };
    };
    @ob_end_clean();
    if(ini_get('zlib.output_compression'))
    ini_set('zlib.output_compression', 'Off');
    header('Content-Type: ' . $mime_type);
    header('Content-Disposition: attachment; filename="'.$name.'"');
    header("Content-Transfer-Encoding: binary");
    header('Accept-Ranges: bytes');

    if(isset($_SERVER['HTTP_RANGE']))
    {
        list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);
        list($range) = explode(",",$range,2);
        list($range, $range_end) = explode("-", $range);
        $range=intval($range);
        if(!$range_end) {
            $range_end=$size-1;
        } else {
            $range_end=intval($range_end);
        }

        $new_length = $range_end-$range+1;
        header("HTTP/1.1 206 Partial Content");
        header("Content-Length: $new_length");
        header("Content-Range: bytes $range-$range_end/$size");
    } else {
        $new_length=$size;
        header("Content-Length: ".$size);
    }

    $chunksize = 1*(1024*1024);
    $bytes_send = 0;
    if ($file = fopen($file, 'r'))
    {
        if(isset($_SERVER['HTTP_RANGE']))
        fseek($file, $range);

        while(!feof($file) &&
            (!connection_aborted()) &&
            ($bytes_send<$new_length)
        )
        {
            $buffer = fread($file, $chunksize);
            echo($buffer);
            flush();
            $bytes_send += strlen($buffer);
        }
        fclose($file);
    } else
        die('Error - can not open file.');
    die();
}
set_time_limit(0);

/*set your folder*/
$file_path='uploads/'."your file";

/*output must be folder/yourfile*/

output_file($file_path, ''."your file".'', $row['type']);

/*back to index.php while downloading*/
header('Location:index.php');
?>
Jundell Agbo
quelle
2
Woher hast du das? Es scheint mächtig
Axel A. García
@ Jundell-Agbo interessant und für eine CRT-Datei?
Fralbo
Dies funktioniert auch für eine sehr große Datei. Tolle Lösung. Aber `$ chunksize = 1 * (1024 * 1024);` ist langsam. Ich habe es mit mehreren Werten versucht und festgestellt, dass "$ chunksize = 8 * (1024 * 1024)" die gesamte verfügbare Bandbreite nutzt.
Linga
16

Wenn Sie eine Datei mit einer Größe herunterladen müssen, die größer als das zulässige Speicherlimit ( memory_limitINI-Einstellung) ist und den PHP Fatal error: Allowed memory size of 5242880 bytes exhaustedFehler verursachen würde , können Sie Folgendes tun:

// File to download.
$file = '/path/to/file';

// Maximum size of chunks (in bytes).
$maxRead = 1 * 1024 * 1024; // 1MB

// Give a nice name to your download.
$fileName = 'download_file.txt';

// Open a file in read mode.
$fh = fopen($file, 'r');

// These headers will force download on browser,
// and set the custom file name for the download, respectively.
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');

// Run this until we have read the whole file.
// feof (eof means "end of file") returns `true` when the handler
// has reached the end of file.
while (!feof($fh)) {
    // Read and output the next chunk.
    echo fread($fh, $maxRead);

    // Flush the output buffer to free memory.
    ob_flush();
}

// Exit to make sure not to output anything else.
exit;
Parziphal
quelle
Ich habe es gerade versucht und es hat meinen Server getötet. Schrieb eine große Anzahl von Fehlern in meine Protokolle.
Daniel Williams
Es wäre nützlich zu wissen, worum es in den Protokollen geht.
Parziphal
Ja, tut mir leid, die Protokolldatei wurde so groß, dass ich sie nur nuklearisieren musste, damit ich sie nicht sehen konnte.
Daniel Williams
Ich musste so etwas auf einem Server einrichten, auf den ich nur sehr eingeschränkten Zugriff hatte. Mein Download-Skript wurde immer wieder auf die Startseite umgeleitet, und ich konnte nicht herausfinden, warum. Jetzt weiß ich, dass das Problem ein Speicherfehler war und dieser Code es behoben hat.
Gavin
Wenn Sie nur verwenden, ob_flush()kann der Fehler auftreten: ob_flush (): Puffer konnte nicht geleert werden. Kein Puffer zum Spülen . wickeln Sie es mit if (ob_get_level() > 0) {ob_flush();}(Referenz stackoverflow.com/a/9182133/128761 )
vee
11

Eine Änderung der oben akzeptierten Antwort, die auch den MIME-Typ zur Laufzeit erkennt:

$finfo = finfo_open(FILEINFO_MIME_TYPE);
header('Content-Type: '.finfo_file($finfo, $path));

$finfo = finfo_open(FILEINFO_MIME_ENCODING);
header('Content-Transfer-Encoding: '.finfo_file($finfo, $path)); 

header('Content-disposition: attachment; filename="'.basename($path).'"'); 
readfile($path); // do the double-download-dance (dirty but worky)
Jure Sah
quelle
4

Der folgende Code ist eine korrekte Methode zum Implementieren eines Download-Dienstes in PHP, wie im folgenden Tutorial erläutert

header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$file_name\"");
set_time_limit(0);
$file = @fopen($filePath, "rb");
while(!feof($file)) {
    print(@fread($file, 1024*8));
    ob_flush();
    flush();
}
Grigor Nazaryan
quelle
1
Sie haben zwei Variablen in diesem Code: file_nameund filePath. Nicht sehr gute Codierungspraxis. Besonders ohne Erklärung. Das Verknüpfen mit einem externen Tutorial ist nicht hilfreich.
Khom Nazid
2

Versuche dies:

header('Content-type: audio/mp3'); 
header('Content-disposition: attachment; 
filename=“'.$trackname'”');                             
readfile('folder name /'.$trackname);          
exit();
nqobile
quelle
2

http://php.net/manual/en/function.readfile.php

Das ist alles was du brauchst. "Monkey.gif" ändert Ihren Dateinamen. Wenn Sie von einem anderen Server herunterladen müssen, wechseln Sie zu "monkey.gif" zu " http://www.exsample.com/go.exe ".

Assasin
quelle
1
Willkommen bei StackOverflow. Es ist immer ratsam, den Code direkt einzuschließen, anstatt einen Link hinzuzufügen, um die Linkprobleme zu vermeiden.
Fremder
1

Sie können auch Downloads streamen, wodurch deutlich weniger Ressourcen verbraucht werden. Beispiel:

$readableStream = fopen('test.zip', 'rb');
$writableStream = fopen('php://output', 'wb');

header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="test.zip"');
stream_copy_to_stream($readableStream, $writableStream);
ob_flush();
flush();

Im obigen Beispiel lade ich eine test.zip herunter (die eigentlich die Android Studio-Zip auf meinem lokalen Computer war). php: // output ist ein schreibgeschützter Stream (wird normalerweise von Echo oder Print verwendet). Danach müssen Sie nur noch die erforderlichen Header festlegen und stream_copy_to_stream (Quelle, Ziel) aufrufen. Die Methode stream_copy_to_stream () fungiert als Pipe, die die Eingabe aus dem Quell-Stream (Lesestream) an den Ziel-Stream (Schreib-Stream) weiterleitet. Außerdem wird vermieden, dass der zulässige Speicher erschöpft ist, sodass Sie tatsächlich größere Dateien herunterladen können als dein PHP memory_limit.

Saud Qureshi
quelle
Was meinst du mit dem Download wird gestreamt?
Parsa Yazdani
1
@ParsaYazdani Dies bedeutet, dass die heruntergeladenen Daten aufgeteilt werden. Weitere Informationen finden Sie im Konzept des Streamings. Hinweis: Dies ist nicht der einzige Stream (Chunk), der die Datei in PHP herunterlädt. Aber es ist sicherlich eine der einfachsten Methoden.
Saud Qureshi
0

Die Antworten über mir funktionieren. Ich möchte jedoch eine Methode zur Durchführung mit GET beisteuern

auf Ihrer HTML / PHP-Seite

$File = 'some/dir/file.jpg';
<a href="<?php echo '../sumdir/download.php?f='.$File; ?>" target="_blank">Download</a>

und download.phpenthält

$file = $_GET['f']; 

header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

$ext = pathinfo($file, PATHINFO_EXTENSION);
$basename = pathinfo($file, PATHINFO_BASENAME);

header("Content-type: application/".$ext);
header('Content-length: '.filesize($file));
header("Content-Disposition: attachment; filename=\"$basename\"");
ob_clean(); 
flush();
readfile($file);
exit;

Dies sollte für alle Dateitypen funktionieren. Dies wird nicht mit POST getestet, könnte aber funktionieren.

Mo Chan
quelle