I am trying to stream/pipe a file to the user's browser through HTTP from FTP. That is, I am trying to print the contents of a file on an FTP server.
This is what I have so far:
public function echo_contents() {
$file = fopen('php://output', 'w+');
if(!$file) {
throw new Exception('Unable to open output');
}
try {
$this->ftp->get($this->path, $file);
} catch(Exception $e) {
fclose($file); // wtb finally
throw $e;
}
fclose($file);
}
$this->ftp->get
looks like this:
public function get($path, $stream) {
ftp_fget($this->ftp, $stream, $path, FTP_BINARY); // Line 200
}
With this approach, I am only able to send small files to the user's browser. For larger files, nothing gets printed and I get a fatal error (readable from Apache logs):
PHP Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 15994881 bytes) in /xxx/ftpconnection.php on line 200
I tried replacing php://output
with php://stdout
without success (nothing seems to be sent to the browser).
How can I efficiently download from FTP while sending that data to the browser at the same time?
Note: I would not like to use file_get_contents('ftp://user:pass@host:port/path/to/file');
or similar.
Found a solution!
Create a socket pair (anonymous pipe?). Use the non-blocking
ftp_nb_fget
function to write to one end of the pipe, andecho
the other end of the pipe.Tested to be fast (easily 10MB/s on a 100Mbps connection) so there's not much I/O overhead.
Be sure to clear any output buffers. Frameworks commonly buffer your output.
(I've never met this problem myself, so that's just a wild guess ; but, maybe... )
Maybe changing the size of the ouput buffer for the "file" you are writing to could help ?
For that, see
stream_set_write_buffer
.For instance :
With this, your code should use a non-buffered stream -- this might help...
Sounds like you need to turn off output buffering for that page, otherwise PHP will try to fit it in all memory.
An easy way to do this is something like:
Put that ahead of your call to ->get(), and I think that will resolve your issue.
Try:
I find with a lot of file operations it's worthwhile letting the underlying OS functionality take care of it for you.
a quick search brought up php’s flush.
this article might also be of interest: http://www.net2ftp.org/forums/viewtopic.php?id=3774
I know this is old, but some may still think it's useful.
I've tried your solution on a Windows environment, and it worked almost perfectly:
I used
STREAM_PF_INET
instead ofSTREAM_PF_UNIX
because of Windows, and it worked flawlessly... until the last chunk, which wasfalse
for no apparent reason, and I couldn't understand why. So the output was missing the last part.So I decided to use another approach:
This worked like a charm with PHP 5.4.5. The bad part is that you can't catch the transferred data, only the chunk size.