PHP tip: How to make a file downloadable through your script

October 10, 2008 – 11:46 am Tags: ,

This seems to be a relatively common question on #zftalk nowadays, so here’s a quick wrapup!

The traditional plain-PHP way

$file = file_get_contents('some.zip');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="some.zip"');
header('Content-Length: ' . strlen($file));
echo $file;

The Zend Framework controller way

//this is inside an action in one of your controllers:
$file = file_get_contents('some.zip');
 
$this->getResponse()
     ->setBody($file)
     ->setHeader('Content-Type', 'application/zip')
     ->setHeader('Content-Disposition', 'attachment; filename="some.zip"')
     ->setHeader('Content-Length', strlen($file));
 
//If using Zend_Layout, we need to disable it:
$this->_helper->layout->disableLayout();
 
//Disable ViewRenderer:
$this->_helper->viewRenderer->setNoRender(true);

In closing

As mentioned in the comments, this approach may not be suitable especially if you deal with very large files. I recommend checking out this post about using mod_xsendfile with PHP, as it solves the issues associated with sending files “manually” through PHP.

Share this:
  1. 13 Responses to “PHP tip: How to make a file downloadable through your script”

  2. Here’s a short explanation of what the ZF code does:

    the getResponse method returns the response object, which contains the headers and the data that will get sent to the user.

    So, we simply use the setBody method, which will set the body (the data) of the response, and the setHeader method, to modify the response to suit our needs.

    You could use the plain-PHP snippet with ZF and append exit; in the end to prevent any other output, but if you ever needed to unit test or something, it would cause problems. Using the response object, we can easily test its contents and headers.

    By Jani Hartikainen on Oct 10, 2008

  3. Informative.

    I was about to make such a post on my blog. I think you published it after yesterday’s conversation on #zftalk.

    By Sudheer on Oct 10, 2008

  4. This is ugly – it breaks HTTP resuming, breaks caching.

    Instead of very problematic (esp. in IE) Content-Disposition you could use mod_rewrite or /file.php/file.zip trick.

    By kL on Oct 10, 2008

  5. I don’t see how this differs from using file.php/file.zip, if that’s simply a PHP script which sends you the file. Also, Content-Disposition works perfectly well at least from what I’ve tested it.

    Perhaps you should explain your comment better next time.

    By Jani Hartikainen on Oct 11, 2008

  6. In my opinion file_get_contents is the total wrong way for handling any kind of downloading files, it will consume a lot of memory, and if the file is bigger than memory_limit, it will break the script.

    If someone wants to handle file-downloads through php, it should a least support resuming and I think using Zend_View is even in an Zend Framework Enviroment the wrong way, because it forces sending all content in one step, no chance for reading and sending data from a source in a loop.

    By robo47 on Oct 11, 2008

  7. That’s a good point. Using filesize() and fpassthru() are probably better options if your files are bigger – though you can’t use the response object “correctly” with fpassthru, unless you use output buffering, which would make it equal file_get_contents and nullifying the advantages it has

    By Jani Hartikainen on Oct 11, 2008

  8. I found mod_xsendfile a good solution to the readfile/file_get_contents dilemma. Just send the standard headers along with X-Sendfile and it handles with file transfer. Definitly worth a look…

    By Tom Graham on Oct 11, 2008

  9. file_get_contents? Seriously?

    Web applications are practically front-ends. They run some instructions in the back. You run some Apache threads, each thread is manageable, you get a reasonably performant system. Start loading entire files into memory, you then have to unload them, and your thread is busy for that process. Caching is a real pain too.

    A file should not be downloadable through a script. Create a symlink on the fly, or maybe do some socket business (in Python?) and create a mini-app to handle it. But PHP scripts are not here to serve files over HTTP; that’s what web servers are for.

    By Josh on Oct 27, 2008

  10. thank u…

    By vijay on Jun 26, 2011

  11. Wrong way of doing it… memory limits and timeouts can and will slap you if you do it the way you posted.

    Here’s the correct way to do it:

    [SNIP]
    $file = “somefile.zip”; // Filename
    if(file_exists($file) && is_file($file))
    {
    header(“Cache-control: private”);
    header(“Content-Type: application/octet-stream”);
    header(“Content-Length: “.filesize($file));
    header(“Content-Disposition: filename=$file” . “%20″);
    flush();
    $fd = fopen($file, “r”);
    while(!feof($fd)) {
    echo fread($fd, round(4096));
    flush();
    set_time_limit(30);
    }
    fclose ($fd);
    }
    [/SNIP]

    By Mike Edward Moras (e-sushiâ„¢) on Nov 25, 2012

  12. And for those who wonder how to do it with Symfony2 (in the action):

    $response = new Response($content, 200, array('Content-Type' => 'image/jpeg', 'Content-Disposition' => 'attachment; filename="image.jpg"', 'Content-Length' => '12345'));
    return $response;

    By Darmen on Dec 21, 2012

  1. 2 Trackback(s)

  2. Mar 6, 2009: Sending files better: Apache mod_xsendfile and PHP | CodeUtopia
  3. Jul 15, 2013: Sending files better: Apache mod_xsendfile and PHP

Post a Comment

You can use some HTML (a, em, strong, etc.). If you want to post code, use <pre lang="PHP">code here</pre> (you can replace PHP with the language you are posting)