Simple Force File Download In PHP (Quick & Easy Examples)

Welcome to a quick tutorial on how to force download files in PHP. Need to offer a file as a download, but the browser opens it instead? Don’t worry, we can “fix” this using only a few lines of PHP code.

To force a file download in PHP, we need to:

  1. Output the respective “handle this as a download” HTTP headers.
    • header("Content-Type: application/octet-stream");
    • header("Content-Type: Content-Transfer-Encoding: Binary");
    • header("Content-disposition: attachment; filename=\"MY.FILE\"");
  2. Then, simply read and output the file – readfile("MY.FILE");

That covers the quick basics, but read on for more examples!

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

QUICK SLIDES

 

TABLE OF CONTENTS

Download & Notes Force Download Useful Bits & Links
The End

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

EXAMPLE CODE DOWNLOAD

Click here to download all the example source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

 

FORCE FILE DOWNLOAD

All right, let us now get into the various examples of how to force a file download.

 

1) HTML FORCE DOWNLOAD

1-download.html
<!-- (A) OPEN IMAGE IN BROWSER -->
<a href="ice-cream.jpg">Open</a>
 
<!-- (B) DOWNLOAD "ICE-CREAM.JPG" -->
<a href="ice-cream.jpg" download>Download</a>
 
<!-- (C) DOWNLOAD AS "IC.JPG" -->
<a href="ice-cream.jpg" download="ic.jpg">Download As</a>

Yes, this is just plain old HTML. For you guys who just want to force download a file on the server, the simplest way is to add a download property to the <a> anchor link – Not a single line of PHP code is required. But if you want to hide the original file location or force download a generated file – Follow up with the below examples.

 

2) PHP FORCE FILE DOWNLOAD

2-read-file.php
<?php
// (A) RECOMMENDED IF HANDLING HUGE FILE
set_time_limit(120); // 120 SECONDS

// (B) TARGET FILE TO FORCE DOWNLOAD
$file = "ice-cream.jpg";
$basename = basename($file);

// (C) HTTP HEADERS
header("Content-Type: application/octet-stream");
header("Content-Type: Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"$basename\"");
readfile($file);

This should be pretty self-explanatory. As in the introduction, we output the HTTP download headers first, then use readfile() to output the file.

 

 

3) PHP FORCE DOWNLOAD GENERATED FILE

3-generate-csv.php
<?php
// (A) RECOMMENDED IF HANDLING HUGE FILE
set_time_limit(120); // 120 SECONDS

// (B) DUMMY DATA
$users = [
  [1, "Jon Doe", "jon@doe.com"],
  [2, "Jan Doe", "jan@doe.com"],
  [3, "Jun Doe", "jun@doe.com"],
  [4, "Jay Doe", "jay@doe.com"],
  [5, "Joy Doe", "joy@doe.com"]
];

// (C) HTTP HEADERS
$file = "users.csv"; // DOWNLOAD FILE NAME
header("Content-Type: application/octet-stream");
header("Content-Type: Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"$file\"");
foreach ($users as $u) {
  echo implode(",", $u) . "\r\n";
}

Yes, we can also output the HTTP download headers, then serve data on the fly. This is very useful for all kinds of reports, or even generating an image download.

 

 

USEFUL BITS & LINKS

That’s all for the main tutorial, and here is a small section on some extras and links that may be useful to you.

 

CONTENT HEADERS – WHICH ONE IS CORRECT!?

If you dig around the Internet, you will notice that everyone has kind of different sets of HTTP headers. For example6:

<?php
header("Content-Description: File Transfer");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".basename($file)."\"");
header("Expires: 0");
header("Cache-Control: must-revalidate");
header("Pragma: public");
header("Content-Length: " . filesize($file));

So… who’s right and who’s wrong? That is a rather difficult question to answer – The example in the introduction is correct, this is also correct. The difference here is that it also includes “no-cache” headers, which will force the browser to revalidate the entire download.

To put things in simple terms – Every HTTP header serves a different purpose. It’s up to you to decide what is required, and what works for you. Personally, I go with “it’s correct so long as it works”.

 

LINKS & REFERENCES

 

INFOGRAPHIC CHEAT SHEET

Force File Download In PHP (Click to Enlarge)

 

THE END

Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

Leave a Comment

Your email address will not be published.