Forcing to download a file using PHP

179,915

Solution 1

.htaccess Solution

To brute force all CSV files on your server to download, add in your .htaccess file:

AddType application/octet-stream csv

PHP Solution

header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=example.csv');
header('Pragma: no-cache');
readfile("/path/to/yourfile.csv");

Solution 2

Or you can do this using HTML5. Simply with

<a href="example.csv" download>download not open it</a>

Solution 3

This cannot be done reliably, since it's up to the browser to decide what to do with an URL it's been asked to retrieve.

You can suggest to the browser that it should offer to "save to disk" right away by sending a Content-disposition header:

header("Content-disposition: attachment");

I'm not sure how well this is supported by various browsers. The alternative is to send a Content-type of application/octet-stream, but that is a hack (you're basically telling the browser "I'm not telling you what kind of file this is" and depending on the fact that most browsers will then offer a download dialog) and allegedly causes problems with Internet Explorer.

Read more about this in the Web Authoring FAQ.

Edit You've already switched to a PHP file to deliver the data - which is necessary to set the Content-disposition header (unless there are some arcane Apache settings that can also do this). Now all that's left to do is for that PHP file to read the contents of the CSV file and print them - the filename=example.csv in the header only suggests to the client browser what name to use for the file, it does not actually fetch the data from the file on the server.

Solution 4

Here is a more browser-safe solution:

    $fp = @fopen($yourfile, 'rb');

    if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
    header('Content-Type: "application/octet-stream"');
    header('Content-Disposition: attachment; filename="yourname.file"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header("Content-Transfer-Encoding: binary");
    header('Pragma: public');
    header("Content-Length: ".filesize($yourfile));
}
else
{
    header('Content-Type: "application/octet-stream"');
    header('Content-Disposition: attachment; filename="yourname.file"');
    header("Content-Transfer-Encoding: binary");
    header('Expires: 0');
    header('Pragma: no-cache');
    header("Content-Length: ".filesize($yourfile));
}

fpassthru($fp);
fclose($fp);

Solution 5

Configure your server to send the file with the media type application/octet-stream.

Share:
179,915
Elitmiar
Author by

Elitmiar

Updated on February 11, 2022

Comments

  • Elitmiar
    Elitmiar over 2 years

    I have a CSV file on my server. If a user clicks on a link it should download, but instead it opens up in my browser window.

    My code looks as follows

    <a href="files/csv/example/example.csv">
        Click here to download an example of the "CSV" file
    </a>
    

    It's a normal webserver where I have all of my development work on.

    I tried something like:

    <a href="files/csv/example/csv.php">
        Click here to download an example of the "CSV" file
    </a>
    

    Now the contents of my csv.php file:

    header('Content-Type: application/csv');
    header('Content-Disposition: attachment; filename=example.csv');
    header('Pragma: no-cache');
    

    Now my issue is it's downloading, but not my CSV file. It creates a new file.

  • user6337901
    user6337901 over 14 years
    Using "Content-disposition: attachment" has worked consistently for us in all FF versions, IE6 and IE7.
  • Powerlord
    Powerlord over 14 years
    Why not just change the Content-Type line to Content-Type: application/octet-stream ? It seems a bit redundant to do it in an .htaccess when you're overriding it anyway.
  • TEEAMO
    TEEAMO over 14 years
    that was the inital answer to the question, the question then changed so the second part was the answer to that.
  • Chris Harrison
    Chris Harrison almost 11 years
    This doesn't work in all browsers, with all filetypes. For example, it won't work on the latest (Aug 2013) version of Chrome with PDF files and perhaps csv files too. See my answer for how to get this working in a more reliable way.
  • Sam
    Sam over 9 years
    Unfortunately this isn't supported in IE or Safari: caniuse.com/#feat=download
  • The Quantum Physicist
    The Quantum Physicist almost 9 years
    This model could create a major security flaw. An attacker can use it to download your source file! Use it with caution!!!!
  • Olaf
    Olaf over 8 years
    This looks like some old download script I've written years ago and you're right a hacker could try to find the location of the file. Input validation is also difficult this way. A few days ago I re-published an article with a second examples that is using a database with a hash ID to obtain the file name. It's not a perfect solution, but it points into the right redirection: web-development-blog.com/archives/php-download-file-script
  • Govind Rai
    Govind Rai over 7 years
    Great Answer! I would suggest changing 'Content-Type: application/csv' to 'Content-Type: application/octet-stream' as suggested by @Powerlord. It will be a more well-rounded answer and will help others who are not just interested in having their CSVs downloaded. Just a suggestion! Thanks!
  • Faizan Noor
    Faizan Noor over 7 years
    The best part of adding download attribute is that it will force to download any type of file.
  • Prifulnath
    Prifulnath about 7 years
    Can you please tell me how, if it is a zip file??
  • Chuck Le Butt
    Chuck Le Butt about 7 years
    It's finally supported in Safari!
  • Peter Mortensen
    Peter Mortensen about 6 years
    What makes it nice and clean?
  • DIDIx13
    DIDIx13 about 6 years
    Talking about Null byte injection?
  • DavidScherer
    DavidScherer over 5 years
    Typically you'll only run into issues with content-disposition if you have a default action for downloaded files of a type set for your browser, otherwise browsers will typically follow the recommendation (for more modern versions, ymmv with old/outdated browser versions).
  • DavidScherer
    DavidScherer over 5 years
    I would just use readfile() From the docs If you just want to dump the contents of a file to the output buffer, without first modifying it or seeking to a particular offset, you may want to use the readfile(), which saves you the fopen() call.
  • Randy
    Randy about 5 years
    Despite the need for suggested additions for security, this really is the best approach IMO. I've 8used something like this for years to allow visitors to either view or download PDF file product documentation, and now that most browsers will "play" (rather than download) an MP3, MP4, or other media file, this is the most reliable way of ensuring a download. It also makes it easy to add logging capability, so you can easily see how many people have downloaded, when they did it, and even log IP addresses. Too bad this didn't receive more up-votes! @Olaf I think I've used yours before!
  • Václav
    Václav about 5 years
    Love this. Saved me another night trying to figure out how to work with those damn php download headers.
  • Marco Demaio
    Marco Demaio almost 5 years
    Avoid using Content-Type: application/force-download, read more: stackoverflow.com/a/10616753/260080
  • Marco Demaio
    Marco Demaio almost 5 years
    FYI the standard HTTP Content-Type header for CSV is text/csv, not application/csv.
  • Dima L.
    Dima L. almost 5 years
    Replaced application/force-download with application/octet-stream
  • Radon8472
    Radon8472 about 2 years
    If you change the contenttype your pc will not longer know that this is a csv file, and your default csv programm is not offered for opening this file.