Come forzare il download di file con PHP


125

Voglio richiedere che un file venga scaricato quando l'utente visita una pagina web con PHP. Penso che abbia qualcosa a che fare con file_get_contents, ma non sono sicuro di come eseguirlo.

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

Dopo aver scaricato un file con header(location)esso non viene reindirizzato a un'altra pagina. Si ferma e basta.



Risposte:


234

Leggi la documentazione sulla funzione PHP incorporata readfile

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

Assicurati anche di aggiungere il tipo di contenuto appropriato in base al tuo file application / zip, application / pdf ecc. - ma solo se non vuoi attivare la finestra di dialogo save-as.


6
Perché è necessario?
giovanni

Voglio dire, se è diverso dal file .exe che solo .Else questo dovrebbe funzionare bene.
Pit Digger

7
Non dimenticare di svuotare ;-) ob_clean (); sciacquone(); / * prima di * / readfile ($ file_url);
Ash

Quindi, se hai un file grande da 10 GB, php prova a caricare l'intero file?
GDY

exit () dovrebbe essere chiamato alla fine per evitare potenziali problemi (parlando per esperienza :-)
ykay dice 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(); 
?>

Oppure, quando il file non è apribile con il browser, puoi semplicemente usare l' Locationintestazione:

<?php header("Location: http://example.com/go.exe"); ?>

1
il nome del file nell'intestazione non dovrebbe essere $file(che contiene la parte http) ma un nome di file valido.
Fabio

1
Non esiste alcun tipo di supporto dell'applicazione / download forzato ; usa invece application / octet-stream .
Gumbo

funziona molto bene! Ma aggiunge 'al nome del file memorizzato. Si prega di utilizzare: filename = ". Basename ($ file));
harry4516

@ harry4516 modificato in base alla tua scoperta
Marek Sebera

Non funziona caro in caso di immagine png per me. Grazie.
Kamlesh

40
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\""); 
echo readfile($url);

è corretto

o meglio uno per il tipo di file exe

header("Location: $url");

@Fabio: Aggiungerà più detials
genesis

È stato semplicissimo. Ha funzionato. A cosa serve file-Get_contents? Solo curioso. Grazie.
giovanni

per scaricarlo sul tuo server
genesis

10
È sufficiente rimuovere "echo" prima di "readfile ()" poiché il valore restituito è specificato come "Restituisce il numero di byte letti dal file. Se si verifica un errore, viene restituito FALSE ea meno che la funzione non sia stata chiamata come @readfile (), viene stampato un messaggio di errore. ". Quindi ti ritroverai con il contenuto del file + numero intero alla fine del contenuto.
Mladen B.

22

Visualizza prima il file e imposta il suo valore 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
Da dove l'hai preso? Sembra potente
Axel A. García

@ jundell-agbo interessante e per un file crt?
fralbo

Funziona anche per un file molto grande. Ottima soluzione. Ma `$ chunksize = 1 * (1024 * 1024);` è lento. Ho provato con più valori e ho notato che `$ chunksize = 8 * (1024 * 1024);` utilizza tutta la larghezza di banda disponibile.
Linga

16

Se devi scaricare un file con una dimensione maggiore del limite di memoria consentito ( memory_limitimpostazione ini), che causerebbe l' PHP Fatal error: Allowed memory size of 5242880 bytes exhaustederrore, puoi farlo:

// 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;

L'ho appena provato e ha ucciso il mio server. Ha scritto un numero enorme di errori nei miei log.
Daniel Williams

Sarebbe utile sapere di cosa trattavano i log.
Parziphal

Sì, scusa, il file di registro è diventato così grande che ho dovuto solo bombardarlo, quindi non sono riuscito a vederlo.
Daniel Williams

Dovevo impostare qualcosa di simile su un server a cui avevo un accesso molto limitato. Il mio script di download continuava a reindirizzare alla home page e non riuscivo a capire perché. Ora so che il problema era un errore di memoria e questo codice lo ha risolto.
Gavin

Se lo usi ob_flush(), potrebbe esserci l'errore: ob_flush (): flush buffer non riuscito. Nessun buffer da scaricare . avvolgerlo con if (ob_get_level() > 0) {ob_flush();}(riferimento stackoverflow.com/a/9182133/128761 )
vee

11

Una modifica della risposta accettata sopra, che rileva anche il tipo MIME in runtime:

$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

Il codice seguente è un modo corretto di implementare un servizio di download in php come spiegato nel seguente tutorial

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
Hai due variabili in questo codice: file_namee filePath. Pratica di codifica non molto buona. Soprattutto senza spiegazioni. Il collegamento a un tutorial esterno non è utile.
Khom Nazid

2

prova questo:

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


1

Puoi anche eseguire il download in streaming che consumerà molte meno risorse. esempio:

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

Nell'esempio sopra, sto scaricando un test.zip (che in realtà era lo zip di Android Studio sul mio computer locale). php: // l'output è un flusso di sola scrittura (generalmente utilizzato da echo o print). dopodiché, devi solo impostare le intestazioni richieste e chiamare stream_copy_to_stream (sorgente, destinazione). Il metodo stream_copy_to_stream () agisce come una pipe che prende l'input dal flusso sorgente (flusso di lettura) e lo convoglia al flusso di destinazione (flusso di scrittura) ed evita anche il problema dell'esaurimento della memoria consentita in modo da poter effettivamente scaricare file più grandi rispetto al tuo PHP memory_limit.


Cosa intendi con il download in streaming?
Parsa Yazdani

1
@ParsaYazdani Significa che i dati scaricati verranno suddivisi in blocchi. Per favore guarda al concetto di streaming per maggiori dettagli. Nota: questo non è l'unico per eseguire lo streaming (blocco) del download del file in PHP. Ma è sicuramente uno dei metodi più semplici.
Saud Qureshi

0

Le risposte sopra di me funzionano. Ma vorrei contribuire con un metodo su come eseguirlo usando GET

nella tua pagina html / php

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

e download.phpcontiene

$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;

questo dovrebbe funzionare su qualsiasi tipo di file. questo non è testato utilizzando POST, ma potrebbe funzionare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.