Comment forcer le téléchargement de fichiers avec PHP

125

Je veux exiger qu'un fichier soit téléchargé lorsque l'utilisateur visite une page Web avec PHP. Je pense que cela a quelque chose à voir avec file_get_contents, mais je ne sais pas comment l'exécuter.

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

Après avoir téléchargé un fichier avec, header(location)il ne redirige pas vers une autre page. Ça s'arrête juste.

John
la source
duplication possible du téléchargement de la page
hakre
duplication possible de Forcer à télécharger un fichier en PHP
utilisateur

Réponses:

234

Lisez la documentation sur la fonction PHP intégrée 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); 

Assurez-vous également d'ajouter le type de contenu approprié en fonction de votre fichier application / zip, application / pdf, etc. - mais uniquement si vous ne souhaitez pas déclencher la boîte de dialogue Enregistrer sous.

Fosseur
la source
6
Pourquoi est-ce nécessaire?
john
Je veux dire si son autre que le fichier .exe que seulement .Else cela devrait fonctionner correctement.
Pit Digger
7
N'oubliez pas de vider ;-) ob_clean (); affleurer(); / * avant * / readfile ($ file_url);
Ash
Donc, si vous avez un gros fichier de 10 Go, php essaie de charger tout ce fichier?
GDY
exit () doit être appelé à la fin pour éviter tout problème potentiel (en parlant d'expérience :-)
ykay dit Réintégrer 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(); 
?>

Ou, lorsque le fichier ne peut pas être ouvert avec le navigateur, vous pouvez simplement utiliser l'en- Locationtête:

<?php header("Location: http://example.com/go.exe"); ?>
Marek Sebera
la source
1
Le nom de fichier dans votre en-tête ne doit pas être $file(qui contient la partie http) mais un nom de fichier valide.
Fabio
1
Il n'y a pas de type de support d' application / téléchargement forcé ; utilisez plutôt application / octet-stream .
Gumbo
fonctionne très bien! Mais ajoute 'au nom de fichier stocké. Veuillez utiliser: filename = ". Basename ($ file));
harry4516
@ harry4516 modifié selon votre conclusion
Marek Sebera
Ne fonctionne pas cher en cas d'image png pour moi. Merci.
Kamlesh
40
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\""); 
echo readfile($url);

est correct

ou meilleur pour le type de fichiers exe

header("Location: $url");
genèse
la source
@Fabio: ajoutera plus de détails
genesis
C'était super facile. Ça a marché. À quoi sert alors file-Get_contents? Juste curieux. Merci.
john
pour le télécharger sur votre serveur
genesis
10
Supprimez simplement 'echo' avant 'readfile ()' car la valeur de retour est spécifiée comme "Renvoie le nombre d'octets lus dans le fichier. Si une erreur se produit, FALSE est renvoyé et à moins que la fonction n'ait été appelée comme @readfile (), un message d'erreur est imprimé. ". Vous vous retrouverez donc avec le contenu du fichier + le nombre entier à la fin du contenu.
Mladen B.
22

Affichez d'abord votre fichier et définissez sa valeur dans 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
la source
2
D'où avez-vous obtenu cela? Cela semble puissant
Axel A. García
@ jundell-agbo intéressant et pour un fichier crt?
fralbo
Cela fonctionne également pour un très gros fichier. Excellente solution. Mais` $ chunksize = 1 * (1024 * 1024); `est lent. J'ai essayé avec plusieurs valeurs et j'ai remarqué que `$ chunksize = 8 * (1024 * 1024);` utilise toute la bande passante disponible.
Linga
16

Si vous devez télécharger un fichier d'une taille supérieure à la limite de mémoire autorisée ( memory_limitparamètre ini), ce qui provoquerait l' PHP Fatal error: Allowed memory size of 5242880 bytes exhaustederreur, vous pouvez le faire:

// 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
la source
J'ai juste essayé ça et ça a tué mon serveur. J'ai écrit un grand nombre d'erreurs dans mes journaux.
Daniel Williams
Il serait utile de savoir de quoi parlent les journaux.
Parziphal
Ouais désolé, le fichier journal est devenu si gros que je devais juste le bombarder, donc je n'ai pas pu le voir.
Daniel Williams
Je devais configurer quelque chose comme ça sur un serveur auquel j'avais un accès très limité. Mon script de téléchargement continuait à rediriger vers la page d'accueil et je ne pouvais pas comprendre pourquoi. Maintenant, je sais que le problème était une erreur de mémoire et ce code l'a résolu.
Gavin le
Si vous utilisez simplement, ob_flush()il peut y avoir l'erreur: ob_flush (): impossible de vider le tampon. Aucun tampon à vider . enveloppez-le avec if (ob_get_level() > 0) {ob_flush();}(Référence stackoverflow.com/a/9182133/128761 )
vee
11

Une modification de la réponse acceptée ci-dessus, qui détecte également le type MIME à l'exécution:

$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
la source
4

Le code suivant est un moyen correct d'implémenter un service de téléchargement en php comme expliqué dans le tutoriel suivant

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
la source
1
Vous avez deux variables dans ce code: file_nameet filePath. Pas très bonne pratique de codage. Surtout sans explication. Lien vers un didacticiel externe inutile.
Khom Nazid
2

essaye ça:

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

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

C'est tout ce dont vous avez besoin. "Monkey.gif" changez le nom de votre fichier. Si vous devez télécharger à partir d'un autre serveur, "monkey.gif" remplacez-le par " http://www.exsample.com/go.exe "

Assasin
la source
1
Bienvenue dans StackOverflow. Il est toujours conseillé d'inclure le code directement au lieu d'ajouter un lien pour éviter les problèmes de lien.
Stranger
1

Vous pouvez également diffuser des téléchargements qui consommeront beaucoup moins de ressources. exemple:

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

Dans l'exemple ci-dessus, je télécharge un test.zip (qui était en fait le zip du studio Android sur ma machine locale). php: // output est un flux en écriture seule (généralement utilisé par echo ou print). après cela, il vous suffit de définir les en-têtes requis et d'appeler stream_copy_to_stream (source, destination). La méthode stream_copy_to_stream () agit comme un tube qui prend l'entrée du flux source (flux de lecture) et la dirige vers le flux de destination (flux d'écriture) et évite également le problème de la mémoire autorisée épuisée afin que vous puissiez réellement télécharger des fichiers plus gros que votre PHP memory_limit.

Saud Qureshi
la source
Qu'entendez-vous par le téléchargement est diffusé?
Parsa Yazdani
1
@ParsaYazdani Cela signifie que les données téléchargées seront fragmentées. Veuillez consulter le concept de streaming pour plus de détails. Remarque: ce n'est pas le seul à streamer (chunk) télécharger le fichier en PHP. Mais c'est sûrement l'une des méthodes les plus simples.
Saud Qureshi
0

Les réponses ci-dessus fonctionnent. Mais, j'aimerais contribuer à une méthode sur la façon de l'exécuter en utilisant GET

sur votre page html / php

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

et download.phpcontient

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

cela devrait fonctionner sur tous les types de fichiers. cela n'est pas testé avec POST, mais cela pourrait fonctionner.

Mo Chan
la source