Php stream file to browser

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?

Php stream file to browser

animuson

52.6k28 gold badges139 silver badges145 bronze badges

asked Aug 2, 2011 at 15:56

Filo StacksFilo Stacks

1,9212 gold badges20 silver badges20 bronze badges

1

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


answered Aug 2, 2011 at 16:01

diagonalbatmandiagonalbatman

17k3 gold badges29 silver badges31 bronze badges

3

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:

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.

answered Nov 6, 2014 at 16:43

5

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)
\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.

answered Aug 2, 2011 at 16:01

ridrid

58.5k30 gold badges145 silver badges189 bronze badges

2

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

And it works very well for me

   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();

answered Oct 6, 2016 at 13:52

Php stream file to browser

0

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.

answered Mar 25, 2020 at 14:05

Php stream file to browser

GillyGilly

8,8345 gold badges31 silver badges33 bronze badges

1

Not the answer you're looking for? Browse other questions tagged php download or ask your own question.