1
votes

I have a PHP-server that serves audio-files by streaming them from an FTP-server not publicly available.

After sending the approriate headers, I just stream the file to the client using ftp_get like this:

ftp_get($conn, 'php://output', $file, FTP_BINARY);

For reasons that has to do with Range headers, I must now offer to only send a part of this stream:

$start = 300; // First byte to stream
$stop = 499; // Last byte to stream (the Content-Length is then $stop-$start+1)

I can do it by downloading the entire content temporarily to a file/memory, then send the desired part to the output. But since the files are large, that solution will cause a delay for the client who has to wait for the file to first be downloaded to the PHP-server before it even starts to download to the client.

Question:

How can I start streaming to php://output from an FTP-server as soon as the first $start bytes have been discarded and stop streaming when I've reached the '$stop' byte?

1
ftp_nb_get(), it's last parameter $resumepos and ftp_nb_continue() might perhaps be of use.user555
@user555 I had missed $resumepos! Yes, it surely helps me to handle where I start from. It doesn't allow me to download a certain number of bytes though.ANisus
In the while ($ret == FTP_MOREDATA) loop maybe you could count the received bytes somehow, however the PHP docs don't mention how many bytes are downloaded on every ftp_nb_continue(). If I had to guess by looking at the FTP extension source code I would say that ftp_nb_continue() downloads as many bytes as FTP_BUFSIZE is set to, default being 4096. For full control I think it would be best to role your own FTP client in PHP, using REST and count the bytes received.user555
@user555 I was able to make it work :) . You can see my answer on how I did it. Thanks for your suggestions!ANisus

1 Answers

1
votes

Instead of using PHP's FTP extension (eg. ftp_get), it is possible to open a stream using PHP's built-in FTP wrapper.

The following code would stream parts of an FTP-file to php://output:

$start = 300; // First byte to stream
$stop = 499; // Last byte to stream
$url = "ftp://username:[email protected]/path/file.mp3";

$ctx = stream_context_create(array('ftp' => array('resume_pos' => $start)));
$fin = fopen($url, 'r', false, $ctx);
$fout = fopen('php://output', 'w');

stream_copy_to_stream($fin, $fout, $stop-$start+1);

fclose($fin);
fclose($fout);

While stream_copy_to_stream has an $offset parameter, using it resulted in an error because the stream was not seekable. Using the context option resume_pos worked fine however.