I'm working on a PHP script which generates large (multi-MB) output on the fly without knowing the length in advance. I am writing directly to php://output via fwrite() and have tried both standard output and using Transfer-Encoding: chunked (encoding the chunks as required) but no matter what I try the browser waits until all the data is written before displaying a download dialog. I have tried flush()ing too after the headers and after each chunk but this also makes no difference.
I'm guessing that Apache is caching the output as the browser would normally display after receiving a few kB from the server.
Does anyone have any ideas on how to stop this caching and flush the data to the browser as it is generated?
Thanks,
J
First of all, like BlaM mentioned in his comment, if in the PHP configuration OutputBuffering is enabled, it wont work, so it would be useful to know your phpinfo().
Next thing, try if it works with a big file that is stored on yor webserver, output it usinf readfile. And, together with this, check if you send the correct headers. Hints on how to readfile() and send the correct headers a provided here: StackOverflow: How to force a file download in PHP
And while you are at it, call ob_end_flush() or ob_end_clean() at the top of your script.
Related
We're using a normal PHP download script (with headers etc) to serve files to users.
The issue however is that with some browsers and large downloads the download script is requested multiple times. NGINX logs show the requests with a 206 status code, (suggesting chunked streaming?) which is strange because we don't serve any streamable content?
Regardless, this means the download script is requested multiple times and thus the MySQL function of +1'ing the download counter for the file is run multiple times per download.
We tried using sessions, but seeing as the download is severed from an external server + domain we have no way to clear said sessions after they're set.
We're using Laravel with NGINX + MySQL, any help would be appreciated. Thanks!
Looking at the spec and the headers for the request which would ultimately result in a 206 response, there was one header which struck out which looks like it would be perfect.
The header in question is the Content-Range header which could look like the following:
Content-Range: bytes 21010-47021/47022
What this is saying is it wants to grab bytes 21010-47021 out of 47022 bytes. All you should need to be worried about is the first number here and if it's 0 or not. If the header was set and the first number is 0, you can assume it's just beginning the download and you should increment the counter.
I'm converting php code to hhvm. One page in particular sometimes needs to flush() a status-message to the browser before sending some emails and a few other slow tasks and then updating the status message.
Before hhvm (using php-fpm and nginx) I used:
header('Content-Encoding: none;');
echo "About to send emails...";
if (ob_get_level() > 0) { ob_end_flush(); }
flush();
// Emails sent here
echo "Emails sent.";
So the content-encoding stops gzip being used, then the flush sends the first message, then the second message is sent when the page ends.
Using HHVM (and nginx), setting the Content-encoding header works (it shows up in the browser), but either hhvm or nginx is ignoring it and sending the page as gzipped content, so the browser interprets the content-encoding=none with binary data.
How can I disable gzip inside php code on HHVM?
(I know I could turn it off in the config files, but I want it kept on for nearly every page load except a few that will run slower.)
While my suggestion would be to have different nginx location paths with different gzip configuration, here's a better alternative solution to achieve what you want to happen.
Better Solution:
It is often referred to as bad practice to keep a connection open (and the browser loading bar spinning) while you're doing work in the background.
Since PHP 5.3.3 there is a method fastcgi_finish_request() which flushes the data and closes the connection, while it continues to work in the background.
Now, this is unfortunately not supported yet on HHVM. However, there is an alternative way of doing this.
HHVM alternative:
You can use register_postsend_function('function_name'); instead. This closes the connection, and the given function will be executed in the background.
Here is an example:
<?php
echo "and ...";
register_postsend_function(function() {
echo "... you should not be seeing this";
sleep("10"); // do a lot of work
});
die();
I work on a PHP project and I use flush().
I did a lot of search and found that PHP sends long outputs of scripts to the browser in chunk parts and does not send all the huge data when the script terminates.
I want to know the size of this data, I mean how many bytes the output must be for PHP to send them to browser.
It's not only PHP that chunks the data; it's actually the job of Apache (or Tomcat etc) to do this. That's why the default is to turn off the "chunking" in PHP and leave it to Apache. Even if you force a flush from PHP, it still can get trapped by Apache. From the manual:
flush() may not be able to override the buffering scheme of your web
server and it has no effect on any client-side buffering in the
browser. It also doesn't affect PHP's userspace output buffering
mechanism. This means you will have to call both ob_flush() and
flush() to flush the ob output buffers if you are using those.
There's a Wikipedia article on transfer encoding / chunking: http://en.wikipedia.org/wiki/Chunked_transfer_encoding
Apache gets more complicated with GZIP or deflate encoding; you'll need to hit an apache server as to how you chan configure it.
i think you are wrong
see this code
echo str_repeat(' ',1024);
for($i=0;$i<10;$i++){
echo $i;
flush();
sleep(1);
if you run it see that every 1 byte sent to browser and print
//the str_repeat is for browser buffer for showing data and nothing else
I have a web application on a site that takes a while (~10 seconds) to complete a portion of the page near the bottom - it has been as optimized as it can be, and caching is not an option.
We have compression enabled on the server via an .htaccess directive SetOutputFilter DEFLATE the problem is this causes the whole page to be held until completion before it starts outputting to the user, this is not optimal as the user sees nothing until the page completes.
I have also tried it via the php ob_start("ob_gzhandler"); method.
Currently I have a <FilesMatch > in my .htaccess restricting this specific script from being compressed.
Basically my question is this - Is there a way to say chunk gzip or deflate so that the user gets it in pieces, so they can see that the page has begun loading?
I would say: no. I think there is now way provided by HTTP.
If you are using the ob_start("ob_gzhandler") method, you can do this - you need to look at the flush and ob_flush functions.
Some sample code - try loading with curl, or use fiddler to inspect the actual http responses
<?php
ob_start('ob_gzhandler');
print "chunk 1";
ob_flush();
flush();
sleep(2);
print "chunk 2";
ob_end_flush();
Unfortunately, browsers don't seem to display this in chunks - I think this is because the data of each chunk is too small. You can verify this effect by calling wget -O - -q http://chunktest/chunktest.php on your test file.
There are some more useful resources here
If the page is that long of a load time, the creative way to handle it is to use a very quick loading page with an ajax call to that long-loading content on the page. We do this for the pages that pull detailed member usage statistics... Other sites, like Adsense for example, do this on their reports page.
PHP Echo or Print functions does not show anything when php is busy with something (like when surfing the web with curl or something like that).
Later i discovered php does show the output when you execute your php on the command line:
php myscript.php
But right now i don't get any outputs from command line too!
Is there any kind of tricks or setting should be done to make php show the outputs?
Chances are, it's caching the results (both in PHP and the web server) and not actually sending them to the browser yet. the best suggestion I can give is this chunk from my code:
/**
* Act as a "breakpoint" in the code, forcing everything to be flushed to the browser
*/
function breakpoint() {
global $debug;
if ($debug) { // So that it doesn't slow down extracts
ob_flush();
flush();
sleep(.1);
}
}
The $debug stuff is if we're running the page in debug mode, specific to our website.
The two main thing you need are ob_flush(), which will send PHP's buffer to the web server's buffer, and flush() which will cause the server to dump to the browser. (Note: if the browser caches before displaying anything, nothing can prevent this) The sleep is there to help make sure it doesn't get overloaded, and has a chance to flush properly.
See:
http://ca.php.net/manual/en/function.ob-flush.php
and
http://ca.php.net/manual/en/function.flush.php
Both PHP and your web server are likely to be caching the output of echo and print. This will often result in no output until the script completes.
Try looking at flush() to force the output out of PHP, but it still may get held up at the web server, so may not help...