Categories

Download Large Files with PHP

Posted on: March 2, 2015 by Dimitar Ivanov
Introduction

Often a simple task as file downloading may lead to an out of memory. To accomplish successfully file download exists few approaches. Let's see the options:

Send headers

When starting a file download you need to send the proper headers to the browser.

<?php
function sendHeaders($file, $type, $name=NULL)
{
    if (empty($name))
    {
        $name = basename($file);
    }
    header('Pragma: public');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Cache-Control: private', false);
    header('Content-Transfer-Encoding: binary');
    header('Content-Disposition: attachment; filename="'.$name.'";');
    header('Content-Type: ' . $type);
    header('Content-Length: ' . filesize($file));
}
?>

Simple download

Using the file_get_contents() may be it's not the best choise, but should works perfectly for small files. The main disadvantage is that when you store the file contents into a variable, the memory for it is reserved.

<?php
$file = '/path/to/files/photo.jpg';
if (is_file($file))
{
    sendHeaders($file, 'image/jpeg', 'My picture.jpg');
    $string = @file_get_contents($file);
    if ($string !== FALSE) {
        echo $string;
    }
    exit;
}
?>

More advanced download

Using the readfile() will not present any memory issues, even when sending large files, on its own. If you encounter an out of memory error ensure that output buffering is off with ob_get_level().

<?php
$file = '/path/to/files/photo.jpg';
if (is_file($file))
{
    sendHeaders($file, 'image/jpeg', 'My picture.jpg');
    ob_clean();
    flush();
    @readfile($file);
    exit;
}
?>

Chunked download

This is the old fashioned, but still the most right way to download large files with PHP.

<?php
$file = '/path/to/files/photo.jpg';
if (is_file($file))
{
    sendHeaders($file, 'image/jpeg', 'My picture.jpg');
    $chunkSize = 1024 * 1024;
    $handle = fopen($file, 'rb');
    while (!feof($handle))
    {
        $buffer = fread($handle, $chunkSize);
        echo $buffer;
        ob_flush();
        flush();
    }
    fclose($handle);
    exit;
}
?>
Conclusion

Basically all the presented three methods can be used to force downloading a file, but when it comes to large files the chunked download is the most right way.

Make your website more secure by using the HTTP Headers for Wordpress, and never face a cross-origin issue again. Oh yes, it's FREE.
See also
Social sharing

If you have questions about downloading large files in small chunks in PHP, leave a comment below. And do not be shy to share this article. Thanks for reading.


2 Comments

Gabriel
The 3 options uses allow_url_fopen, so it's very risk. What about CURL ?
Dimitar Ivanov
@Gabriel
The allow_url_fopen option need to be enabled if the filename "specifies a registered protocol, and that protocol is registered as a network URL", e.g. local files doesn't need it.

If you need to download a remote file and have concerns of enabling the allow_url_fopen option, cURL could be used as an alternative.

Leave a comment

Captcha