Streaming a large file using PHP

php read large file in chunks
php stream
php download large file from url
php fopen
php download file
php stream video
php split large files
php file download script

I have a 200MB file that I want to give to a user via download. However, since we want the user to only download this file once, we are doing this:

echo file_get_contents('http://some.secret.location.com/secretfolder/the_file.tar.gz');

to force a download. However, this means that the whole file has to be loaded in memory, which usually doesn't work. How can we stream this file to them, at some kb per chunk?


Try something like this (source http://teddy.fr/2007/11/28/how-serve-big-files-through-php/):

<?php
define('CHUNK_SIZE', 1024*1024); // Size (in bytes) of tiles chunk

// Read a file and display its content chunk by chunk
function readfile_chunked($filename, $retbytes = TRUE) {
    $buffer = '';
    $cnt    = 0;
    $handle = fopen($filename, 'rb');

    if ($handle === false) {
        return false;
    }

    while (!feof($handle)) {
        $buffer = fread($handle, CHUNK_SIZE);
        echo $buffer;
        ob_flush();
        flush();

        if ($retbytes) {
            $cnt += strlen($buffer);
        }
    }

    $status = fclose($handle);

    if ($retbytes && $status) {
        return $cnt; // return num. bytes delivered like readfile() does.
    }

    return $status;
}

// Here goes your code for checking that the user is logged in
// ...
// ...

if ($logged_in) {
    $filename = 'path/to/your/file';
    $mimetype = 'mime/type';
    header('Content-Type: '.$mimetype );
    readfile_chunked($filename);

} else {
    echo 'Tabatha says you haven\'t paid.';
}
?>

How to Read Big Files with PHP (Without Killing Your Server , Christopher Pitt shows how to read and write large files efficiently, using of streams and generators to remove an entire category of application  I don't recommend this for large files, as the original question states. Smaller files work fine, but I'm using fpassthru on a large file and my download died because "allowed memory size was exhausted". – Magmatic Jul 27 '16 at 18:53


Use fpassthru(). As the name suggests, it doesn't read the entire file into memory prior to sending it, rather it outputs it straight to the client.

Modified from the example in the manual:

<?php

// the file you want to send
$path = "path/to/file";

// the file name of the download, change this if needed
$public_name = basename($path);

// get the file's mime type to send the correct content type header
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $path);

// send the headers
header("Content-Disposition: attachment; filename=$public_name;");
header("Content-Type: $mime_type");
header('Content-Length: ' . filesize($path));

// stream the file
$fp = fopen($path, 'rb');
fpassthru($fp);
exit;

If you would rather stream the content directly to the browser rather than a download (and if the content type is supported by the browser, such as video, audio, pdf etc) then remove the Content-Disposition header.

A Guide to Streams in PHP: In-Depth Tutorial With Examples, So working with large sets of data presents problems. Using streams allows you to interact with very large files in predictable and efficient ways. Streams provide on-demand access to data. This means you don’t need to load the entire contents of your dataset into memory before processing can start. Without streams, opening a 20MB file will consume 20MB of memory. Most installations of PHP are set to use little memory—generally around 64MB.


Take a look at the example from the manual page of fsockopen():

$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {
    $out = "GET / HTTP/1.1\r\n";
    $out .= "Host: www.example.com\r\n";
    $out .= "Connection: Close\r\n\r\n";
    fwrite($fp, $out);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}

This will connect to www.example.com, send a request then get and echo the response in 128 byte chunks. You may want to make it more than 128 bytes.

readfile - Manual, Always using MIME-Type 'application/octet-stream' is not optimal. To anyone that's had problems with Readfile() reading large files into memory the problem  Imran Latif dives deep into streaming and output buffering in PHP, explaining the ins and outs on various demos. Tune in to streamify your app.


I found this method in http://codesamplez.com/programming/php-html5-video-streaming-tutorial

And it works very well for me

   <?php

class VideoStream
{
    private $path = "";
    private $stream = "";
    private $buffer = 102400;
    private $start  = -1;
    private $end    = -1;
    private $size   = 0;

    function __construct($filePath) 
    {
        $this->path = $filePath;
    }

    /**
     * Open stream
     */
    private function open()
    {
        if (!($this->stream = fopen($this->path, 'rb'))) {
            die('Could not open stream for reading');
        }

    }

    /**
     * Set proper header to serve the video content
     */
    private function setHeader()
    {
        ob_get_clean();
        header("Content-Type: video/mp4");
        header("Cache-Control: max-age=2592000, public");
        header("Expires: ".gmdate('D, d M Y H:i:s', time()+2592000) . ' GMT');
        header("Last-Modified: ".gmdate('D, d M Y H:i:s', @filemtime($this->path)) . ' GMT' );
        $this->start = 0;
        $this->size  = filesize($this->path);
        $this->end   = $this->size - 1;
        header("Accept-Ranges: 0-".$this->end);

        if (isset($_SERVER['HTTP_RANGE'])) {

            $c_start = $this->start;
            $c_end = $this->end;

            list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
            if (strpos($range, ',') !== false) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $this->start-$this->end/$this->size");
                exit;
            }
            if ($range == '-') {
                $c_start = $this->size - substr($range, 1);
            }else{
                $range = explode('-', $range);
                $c_start = $range[0];

                $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
            }
            $c_end = ($c_end > $this->end) ? $this->end : $c_end;
            if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $this->start-$this->end/$this->size");
                exit;
            }
            $this->start = $c_start;
            $this->end = $c_end;
            $length = $this->end - $this->start + 1;
            fseek($this->stream, $this->start);
            header('HTTP/1.1 206 Partial Content');
            header("Content-Length: ".$length);
            header("Content-Range: bytes $this->start-$this->end/".$this->size);
        }
        else
        {
            header("Content-Length: ".$this->size);
        }  

    }

    /**
     * close curretly opened stream
     */
    private function end()
    {
        fclose($this->stream);
        exit;
    }

    /**
     * perform the streaming of calculated range
     */
    private function stream()
    {
        $i = $this->start;
        set_time_limit(0);
        while(!feof($this->stream) && $i <= $this->end) {
            $bytesToRead = $this->buffer;
            if(($i+$bytesToRead) > $this->end) {
                $bytesToRead = $this->end - $i + 1;
            }
            $data = fread($this->stream, $bytesToRead);
            echo $data;
            flush();
            $i += $bytesToRead;
        }
    }

    /**
     * Start streaming video content
     */
    function start()
    {
        $this->open();
        $this->setHeader();
        $this->stream();
        $this->end();
    }
}

To use this class, you will have to write simple code like as below:

$stream = new VideoStream($filePath);
$stream->start();

stream_get_contents - Manual, A stream resource (e.g. returned from fopen()) As a result, stream_get_contents​() affects file position in 5.1, and do not affect file position in 5.2 or better. Uppy is a sleek, modular file uploader plugin developed by same folks behind tus protocol. You can use uppy to seamlessly integrate official tus-js-client with a tus-php server. That means we are using php implementation of a server and js implementation of a client.


I ran into this problem as well using readfile() to force a download. The memory problem lies not within readfile, rather with ouput buffering.

Just make sure you switch off output buffering before readfile, and the problem should be fixed.

if (ob_get_level()) ob_end_clean();
readfile($yourfile);

Works for files with a size much larger than the allocated memory limit.

Download large files through PHP, 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 download large files from server, I have changed the below settings in php.ini file: Upload_max_filesize - 1500 M Max_input_time - 1000 Memory_limit - 640M Max_execution_time - 1800 Post_max_size - 2000 M Now, I am able to upload and download 175MB video on server. Since, I have the dedicated server. So, making these changes were easy.


Getting Started With PHP Streams, If you wanted to work with a really big file, you are going to run in to problems. <?​php $iso = file_get_contents(__DIR__ . Downloading a large file using curl. I've been using stream_copy_to_stream instead of If you want to downloads file from the FTP server you can use php FTP


The "right way" to handle file downloads in PHP, I've seen many download scripts written in PHP, from simple one-liners to dedicated classes. Yet, at application/octet-stream"); header("Content-Type: application/download"); For large files, it's useful to allow downloads to be resumed. In this article, we saw very simple steps to upload large files in Web APIs. You can use the same code for any large file. As this is a Web API, we can use this with any client-side application (Windows or Web).


barracudanetworks/ArchiveStream-php: Archive streaming , A library for dynamically streaming dynamic tar or zip files without the need to Large File Support: * * By default, the method add_file_from_path() will send  In this article, we show how to upload videos to a website using PHP. With this upload feature, you upload videos to whatever folder you specify in the code. Once you have a video selected to upload, you press the 'Upload' button. The video is then uploaded to the folder that you can specify. You can then display this video by the HTML <video> tag.