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.



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.


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"); ?>

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");

@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');
?>

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;

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)

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();
}

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();


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.


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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.