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!

Related Posts:

  • Force File Download in PHP
  • Firefox Freezing up When Downloading Files
  • Game to teach kids binary
  • Posting Twitter Updates via Curl
  • PHP: Export Query Results to a CSV File
  • Download All Documents from Blackboard’s Digital Dropbox
  • Cory Doctorow is the Man
  • Command Line SCP for Windows
  • Upgrading to Vim 7 on Dapper
  • Parsing Excel Files with Perl

  • 2 Responses to “PHP: File Download Script - Straming Binary Data to the Browser”

    1. Gravatar drubin Says: Reply to this comment

      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.

      Posted using Mozilla Firefox Mozilla Firefox 3.0.3 on Windows Windows XP
    2. Gravatar Luke Maciak UNITED STATES Says: Reply to this comment

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

      Posted using Mozilla Firefox Mozilla Firefox 3.0.3 on Ubuntu Linux Ubuntu Linux

    Leave a Reply

    XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <pre lang=""> <em> <i> <strike> <strong>

    [Quote selected]