Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Streaming output to a file and the browser

So, I'm looking for something more efficient than this:

<?php
ob_start();
include 'test.php';
$content = ob_get_contents();

file_put_contents('test.html', $content);

echo $content;
?>

The problems with the above:

  • Client doesn't receive anything until the entire page is rendered
  • File might be enormous, so I'd rather not have the whole thing in memory

Any suggestions?

like image 362
Allain Lalonde Avatar asked Mar 06 '09 16:03

Allain Lalonde


People also ask

How does file streaming work?

How does streaming work? Just like other data that's sent over the Internet, audio and video data is broken down into data packets. Each packet contains a small piece of the file, and an audio or video player in the browser on the client device takes the flow of data packets and interprets them as video or audio.

What is a Web stream?

webstream (plural webstreams) (Internet) A streaming media delivery method which uses the Internet to deliver audio and video playback to the end user.

What is HTML streaming?

Dynamic Caching HTML streaming works by combining the power of Varnish Cache and Lua to cache the <head> of the HTML document while dynamically pulling in the rest of the content through edge side includes. This allows for quick speed wins without the need to make AJAX calls or code changes.

What is a stream in web development?

Readable streams A readable stream is a data source represented in JavaScript by a ReadableStream object that flows from an underlying source — this is a resource somewhere on the network or elsewhere on your domain that you want to get data from.


2 Answers

A better way to do this is to use the first two parameters accepted by ob_start: output_callback and chunk_size. The former specifies a callback to handle output as it's buffered, and the latter specifies the size of the chunks of output to handle.

Here's an example:

$output_file = fopen('test.html', 'w');
if ($output_file === false) {
    // Handle error
}

$write_ob_to_file = function($buffer) use ($output_file) {
    fwrite($output_file, $buffer);

    // Output string as-is
    return false;
};

ob_start($write_ob_to_file, 4096);
include 'test.php';
ob_end_flush();

fclose($output_file);

In this example, the output buffer will be flushed (sent) for every 4096 bytes of output (and once more at the end by the ob_end_flush call). Each time the buffer is flushed, the callback $write_ob_to_file will be called and passed the latest chunk. This gets written to test.html. The callback then returns false, meaning "output this chunk as is". If you wanted to only write the output to file and not to PHP's output stream, you could return an empty string instead.

like image 89
Pontus Horn af Rantzien Avatar answered Oct 18 '22 03:10

Pontus Horn af Rantzien


Interesting problem; don't think I've tried to solve this before.

I'm thinking you'll need to have a second request going from your front-facing PHP script to your server. This could be a simple call to http://localhost/test.php. If you use fopen-wrappers, you could use fread() to pull the output of test.php as it is rendered, and after each chunk is received, output it to the screen and append it to your test.html file.

Here's how that might look (untested!):

<?php
$remote_fp = fopen("http://localhost/test.php", "r");
$local_fp = fopen("test.html", "w");
while ($buf = fread($remote_fp, 1024)) {
    echo $buf;
    fwrite($local_fp, $buf);
}
fclose($remote_fp);
fclose($local_fp);
?>
like image 32
pix0r Avatar answered Oct 18 '22 02:10

pix0r