PHP: File Download Script – Straming Binary Data to the Browser

I don’t think I have posted this snipped of code here yet. It is old as hell, but I use it all over the place lately so I figured I just post it here for future reference.

The script below solves the age old problem of making bunch of files accessible to your users without dumping them in a publicly accessible directory (files, not users). For example, you have bunch of PDF documents you don’t want to be indexed by Google, or directly linked to from other websites. What you want is to allow certain users to download them after logging into your web app and checking their cridentials. How do you do that?

Well, the simplest thing to do is to put them in some directory outside of your web root. Set up .htaccess (or whatever you use) to disallow any connection to that folder from the outside world. The only machine with access to these files should be localhost. Then you do some PHP magic in your application to stream the file into the browser upon successful authentication.

The streaming part is actually pretty straightforward – it is a simple combination of the print and fread commands. The convoluted part is convincing your browser to actually initiate file download instead of just dumping ASCII gibberish onto the page for binary files. This is accomplished by sending bunch of headers to the browser prior to streaming the files. The headers are different for IE and the rest of the world but this is pretty much what I ended up with after a lot of trial and error:

// change these to whatever is appropriate in your code
$my_place = "/path/to/the/file/"; // directory of your file 
$my_file = "filename.ext"; // your file

$my_path = $my_place.$my_file;

header("Pragma: public");
header("Expires: 0");
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0', false);
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
$browser = $_SERVER['HTTP_USER_AGENT'];

if(preg_match('/MSIE 5.5/', $browser) || preg_match('/MSIE 6.0/', $browser))
{
  header('Pragma: private');
  // the c in control is lowercase, didnt work for me with uppercase
  header('Cache-control: private, must-revalidate');
  // MUST be a number for IE
  header("Content-Length: ".filesize($my_path)); 
  header('Content-Type: application/x-download');
  header('Content-Disposition: attachment; filename="'.$my_file.'"');
}
else
{
  header("Content-Length: ".(string)(filesize($my_path)));
  header('Content-Type: application/x-download');
  header('Content-Disposition: attachment; filename="'.$my_file.'"');
}

header('Content-Transfer-Encoding: binary');

if ($file = fopen($my_path, 'rb'))
{
  while(!feof($file) and (connection_status()==0))
  {
     print(fread($file, filesize($my_path));
     flush();
  }
  fclose($file);
}

I found these headers to work for me. Your millage may vary. I tested the script above in IE 6 and 7, Firefox 2.x and 3.x, Konqueror 3.5.8 and Chrome 0.2.149.30 and experienced no problems. As usual, I’m always open to constructive criticism and better solutions in the comments. Let me know what you think!

This entry was posted in Uncategorized. Bookmark the permalink.



5 Responses to PHP: File Download Script – Straming Binary Data to the Browser

  1. drubin Mozilla Firefox Windows says:

    Thanks for the header information.

    I just thought I would mention the fpassthru function it cant just make the code easier to read having a single built in function that outputs the file.

    Reply  |  Quote
  2. Luke Maciak UNITED STATES Mozilla Firefox Ubuntu Linux Terminalist says:

    @drubin: I didn’t think about that. Good catch! Thanks for the input.

    Reply  |  Quote
  3. Arti Ajans TURKEY Internet Explorer Windows says:

    Thanks you too much, this is very good and downloading time hiding file size and content headers. Very nice.

    Reply  |  Quote
  4. liquer SLOVAKIA Opera Windows says:

    It not works in IE6 (I think that only in IE6 SP3 version), as every solution I’ve found on the Internet. It falls, when I want to OPEN the PDF file (not save – then it works). The Adobe Reader notify about error, that it can’t find the file. I haven’t found any working solution for this problem, yet.
    Thanks for any tips to solve this.

    Reply  |  Quote
  5. ASTAPP RUSSIAN FEDERATION Mozilla Firefox Ubuntu Linux says:

    Thanks.
    But these script only works through HTTP. As it should be modified, that he would work through HTTPS?

    Reply  |  Quote

Leave a Reply

Your email address will not be published. Required fields are marked *