开发者

Serve a large file via Zend Framework

In our application, authentication is handled via set of Controller Plugins that validate the user etc.

I want开发者_如何学运维 to serve a large (video) file only to authenticated users- the obvious way to do this is via readfile() in the controller, but I'm finding it hits the PHP memory limit - presumably the output from the controller is buffered somewhere.

How can I turn off buffering just for this one controller?

EDIT: Thanks for all the useful tips about flushing any existing output buffering - I guess I was specifically looking for a way of doing this within the framework though?


Interesting problem... You could try:

// ...
public function largeFileAction()
{
    // this clears all active output buffers
    while (ob_get_level()) {
        ob_end_clean();
    }
    readfile('path/to/large/file');
    exit(); // to prevent further request handling
}
//  ...


Ok, I might be totally wrong here, but I think to have read somewhere OB has to be enabled for ZendLayout and placeholder helpers to work, so you'd have to disable them for the downloadAction (you probably aint gonna need them for serving the file anyway).

Would something like this achieve what you want to do?

class DownloadController
{
    public function downloadAction()
    {
        $this->_helper->layout()->disableLayout();
        $this->_helper->viewRenderer->setNoRender(true);
        // authenticate user if not done elsewhere already
        header( /* ... the usual stuff ... */);
        filepassthru(/* some path outside webroot */);
        exit;
    }
}


As Tyson wrote, your best choice (if you have full control over the server) is to validate users credentials and redirect him (302 temporary redirect) to the URL where he can download the file.

To prevent reuse of this URLs we are using Lighttpd and its mod_secdownload that allows you to generate a hash that is valid for the specified amount of time.

nginx has X-Accel-Redirect and Apache has mod_xsendfile.

If you decide to implement a separate lightweight web server there are other benefits as well (mainly lower memory consumption while serving static files and faster response times).

If you decide to go this route you will either have to add another IP address to the server and bind Apache only to the one IP address, and the other server (lighty of nginx) to the other because they are web servers the both listen on port 80. And changing the port for one of the servers is not a good idea because a lot of people do not have access to higher ports.

If adding another IP address is not an option you can install nginx on port 80 and use it as a reverse proxy to pass the dynamic requests to Apache which can listen on another port and serve all of the static files.


Considering using an external script to output the file, and stream it to the browser using PHP's passthru function.

If on a Linux-based system, you could try something like passthru("cat video_file.flv");

However, a better practice is to avoid this streaming (from within PHP) altogether and issue the client a 301 HTTP redirection to the URL of the actual static resource so that the webserver can handle streaming it directly.


I don't think you can actually. As far as i know php buffers all output before sending it to the requester.

you could increase the memory limit using ini_set()


$handle = fopen('/path/to/file', 'r');
$chunk_size = 8192;

while ($chunk = fread($handle, $chunk_size)) {
    echo $chunk;
    ob_flush();
}

This will probably need some tweaking, such as adding correct headers and reading in binary mode if necessary, but the basic idea is sound. I have used this method successfully to send 50+ MB files, with a 16 MB PHP memory limit.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜