Reputation: 4411
I have a PHP script on my server that is making a request to another server for an image.
The script is accessed just like a regular image source like this:
<img src="http://example.com/imagecontroller.php?id=1234" />
Browser -> Script -> External Server
The script is doing a CURL request to the external server.
Is it possible to "stream" the CURL response directly back to the client (browser) as it is received on the server?
Assume my script is on a slow shared hosting server and the external server is blazing fast (a CDN). Is there a way to serve the response directly back to the client without my script being a bottleneck? It would be great if my server didn't have to wait for the entire image to be loaded into memory before beginning the response to the client.
Upvotes: 29
Views: 46397
Reputation: 1
a) there is no --no-buffer to php curl, it only exists on the CLI version
b) while it's possible to echo and even flush with the writefunction, it does not flush to screen live. ie. if you set a timer / sleep in the callback, then you'll find that instead of each bit from the callback being received and immediately rendered to browser from the flush, the curl_exec() basically holds it back because it still needs to wait for the callback itself to complete for the writefunction to be triggered. So a sleep in the callack of 5 seconds will cause the curl to run and hold for 5 seconds until all the flushed elements will echo'd to buffer then flushed.
eg. in the example below, if the function being called at the url route for the curl is streaming a response - waiting every 2 seconds and echo'ing with a flush to push it through the curl, and you try to capture and parse it through with the following - it will not stream live - because the curl itself is still midcall and does not process the writefunction until the call itself is over. After which time it runs the writefunction as if live but with all the data, so it just renders to the browser, or if you tried to yield it through buffers, yields as a single response.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); //put response into an output var
/// flush each out at a time
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($curl, $data) use (&$result) {
echo $data.' <br><br>';
while (ob_get_contents()) {ob_flush();}flush(); @ob_end_flush();
if(strstr($data,'||--ENDOFDATA--||')){return -1;}
return strlen($data);
});
curl_exec($ch);
curl_close($ch);
return;
There's no way to do proper streaming with curl response, whether you are trying to run the curl as a callback that is passed as the yielding function, or if you are trying to yield from the curl function, to return a generator back to the parent call.
Go with guzzle or something similar instead.
Upvotes: 0
Reputation: 121
Yes you can use the CURLOPT_WRITEFUNCTION flag:
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
Where $ch
is the Curl handler, and $callback
is the callback function name.
This command will stream response data from remote site. The callback function can look something like:
$result = '';
$callback = function ($ch, $str) {
global $result;
$result .= $str;//$str has the chunks of data streamed back.
//here you can mess with the stream data either with $result or $str
return strlen($str);//don't touch this
};
If not interrupted at the end $result
will contain all the response from remote site.
Upvotes: 4
Reputation: 66851
Pass the -N
/--no-buffer
flag to curl
. It does the following:
Disables the buffering of the output stream. In normal work situations, curl will use a standard buffered output stream that will have the effect that it will output the data in chunks, not necessarily exactly when the data arrives. Using this option will disable that buffering.
Note that this is the negated option name documented. You can thus use --buffer to enforce the buffering.
Upvotes: 62
Reputation: 5707
Check out Pascal Martin's answer to an unrelated question, in which he discusses using CURLOPT_FILE for streaming curl responses. His explanation for handling " Manipulate a string that is 30 million characters long " should work in your case.
Hope this helps!
Upvotes: 1