How to force file download with PHP

521,367

Solution 1

Read the docs about built-in PHP function 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); 

Also make sure to add proper content type based on your file application/zip, application/pdf etc. - but only if you do not want to trigger the save-as dialog.

Solution 2

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

Or, when the file is not openable with the browser, you can just use the Location header:

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

Solution 3

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

is correct

or better one for exe type of files

header("Location: $url");

Solution 4

Display your file first and set its value into 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');
?>

Solution 5

In case you have to download a file with a size larger than the allowed memory limit (memory_limit ini setting), which would cause the PHP Fatal error: Allowed memory size of 5242880 bytes exhausted error, you can do this:

// 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;
Share:
521,367
john
Author by

john

Updated on July 08, 2022

Comments

  • john
    john almost 2 years

    I want to require a file to be downloaded upon the user visiting a web page with PHP. I think it has something to do with file_get_contents, but am not sure how to execute it.

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

    After downloading a file with header(location) it is not redirecting to another page. It just stops.

  • Fabio
    Fabio over 12 years
    filename in your header shouldn't be $file (which contains the http part) but a valid filename.
  • Gumbo
    Gumbo over 12 years
    There is no application/force-download media type; use application/octet-stream instead.
  • genesis
    genesis over 12 years
    @Fabio: Will add more detials
  • john
    john over 12 years
    That was super easy. It worked. What is file-Get_contents for then? Just curious. Thanks.
  • genesis
    genesis over 12 years
    to download it to your server
  • Pit Digger
    Pit Digger over 12 years
    I mean if its other than .exe file than only .Else this should work fine.
  • Mladen B.
    Mladen B. almost 11 years
    Just remove 'echo' prior to 'readfile()' since the return value is specified as "Returns the number of bytes read from the file. If an error occurs, FALSE is returned and unless the function was called as @readfile(), an error message is printed.". So you'll end up with the content of the file + integer number at the end of the content.
  • Ash
    Ash about 9 years
    Dont forget to flush ;-) ob_clean(); flush(); /* before */ readfile($file_url);
  • Axel A. García
    Axel A. García almost 8 years
    Where did you get this from? It seems powerful
  • fralbo
    fralbo over 7 years
    @jundell-agbo interesting and for a crt file?
  • Daniel Williams
    Daniel Williams over 6 years
    I just tried this and it killed my server. Wrote a massive number of errors to my logs.
  • Parziphal
    Parziphal over 6 years
    It would be useful to know what the logs were about.
  • Daniel Williams
    Daniel Williams over 6 years
    Yeah sorry, the log file got so big I had to just nuke it, so I didn't get to see.
  • Stranger
    Stranger over 6 years
    Welcome to StackOverflow. It's always advisable to include the code directly instead of adding a link to avoid the link issues.
  • Gavin
    Gavin about 6 years
    I was having to set up something like this on a server that I had very limited access to. My download script kept redirecting to the home page and I couldn't figure out why. Now I know the issue was a memory error and this code solved it.
  • Linga
    Linga almost 6 years
    This is working for a very big file also. Great solution. But` $chunksize = 1*(1024*1024);` is slow. I tried with multiple values and noticed that ` $chunksize = 8*(1024*1024);` is using all the available bandwidth.
  • GDY
    GDY over 5 years
    So if you have a 10GB big file php tries to load that whole file?
  • Arsha
    Arsha over 5 years
    If you just use ob_flush() there might be the error: ob_flush(): failed to flush buffer. No buffer to flush. wrap around it with if (ob_get_level() > 0) {ob_flush();} (Reference stackoverflow.com/a/9182133/128761 )
  • Arsha
    Arsha over 5 years
    Add fclose($fh); at the end before exit. Add header('Content-Length: ' . filesize($file)); to let user know file size. +1 your answer :)
  • harry4516
    harry4516 over 5 years
    works very nice ! But adds ' to the stored filename. Please use: filename=" . basename($file));
  • Marek Sebera
    Marek Sebera over 5 years
    @harry4516 modified according to your finding
  • Khom Nazid
    Khom Nazid almost 5 years
    You have two variables in this code: file_name and filePath. Not very good coding practice. Especially without explanation. Linking to external tutorial not helpful.
  • Parsa Yazdani
    Parsa Yazdani almost 5 years
    What do you mean by the download is streamed?
  • Saud Qureshi
    Saud Qureshi almost 5 years
    @ParsaYazdani It means the downloaded data will be chunked. Please look onto the concept of streaming for more details. Note: this is not the only to stream (chunk) download the file in PHP. But it surely is one of the easiest methods.
  • ykay says Reinstate Monica
    ykay says Reinstate Monica over 4 years
    exit() should be called at the end to avoid any potential problems (speaking from experience :-)
  • Kamlesh
    Kamlesh about 4 years
    i am using an image http path as source of path but file is displayed in binary code instead of downloading by using your code. Not worked for me. Thanks.
  • Kamlesh
    Kamlesh about 4 years
    Not working dear in case of png image for me. Thanks.
  • Marek Sebera
    Marek Sebera about 4 years
    @Kamlesh filename should not matter, this solution is filetype agnostic
  • Mark Howells-Mead
    Mark Howells-Mead over 2 years
    The question asked for a PHP solution. This is an HTML solution.