优化PHP以提供所有文件(而不是Apache)

时间:2010-08-23 22:23:10

标签: php apache file-io

我需要通过PHP发送所有Web资源请求以进行用户身份验证,并且不通过Apache直接提供任何文件。这是我的.htaccess:

# All requests are routed to PHP (images, css, js, everything)
RewriteRule ^(.*)$ index.php?query=$1&%{QUERY_STRING} [L]

然后我处理请求,验证用户是否有权访问资源,然后使用以下PHP读取函数输出任何不需要处理的文件。事实证明,与让Apache做其事情相比,这是非常缓慢的。

有人可以推荐一种方法来帮助我提高性能吗?

static function read($path) {
    if(!File::exists($path)) {
        //echo 'File does not exist.';
        header("HTTP/1.0 404 Not Found");
        return;
    }

    $fileName = String::explode('/', $path);
    if(Arr::size($fileName) > 0) {
        $fileName = $fileName[Arr::size($fileName) - 1];
    }

    $size = File::size($path);
    $time = date('r', filemtime($path));

    $fm = @fopen($path, 'rb');
    if(!$fm) {
        header("HTTP/1.0 505 Internal server error");
        return;
    }

    $begin = 0;
    $end = $size;

    if(isset($_SERVER['HTTP_RANGE'])) {
        if(preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)) {
            $begin = intval($matches[0]);
            if(!empty($matches[1]))
                $end = intval($matches[1]);
        }
    }

    if ($begin > 0 || $end < $size)
        header('HTTP/1.0 206 Partial Content');
    else
        header('HTTP/1.0 200 OK');

    // Find the mime type of the file
    $mimeType = 'application/octet-stream';
    //$finfo = @new finfo(FILEINFO_MIME);
    //print_r($finfo);
    //$fres = @$finfo->file($path);
    //if(is_string($fres) && !empty($fres)) {
       //$mimeType = $fres;
    //}

    // Handle CSS files
    if(String::endsWith('.css', $path)) {
        $mimeType = 'text/css';
    }

    header('Content-Type: '.$mimeType);
    //header('Cache-Control: public, must-revalidate, max-age=0');
    //header('Pragma: no-cache');
    header('Accept-Ranges: bytes');
    header('Content-Length:' . ($end - $begin));
    header("Content-Range: bytes $begin-$end/$size");
    header("Content-Disposition: inline; filename=$fileName");
    header("Content-Transfer-Encoding: binary\n");
    header("Last-Modified: $time");
    header('Connection: close');

    $cur = $begin;
    fseek($fm, $begin, 0);

    while(!feof($fm) && $cur < $end && (connection_status() == 0)) {
        print fread($fm, min(1024 * 16, $end - $cur));
        $cur += 1024 * 16;
    }
}

9 个答案:

答案 0 :(得分:2)

如果使用PHP作为Apache模块,最好的办法就是调用virtual

否则,您将不得不接受readfile

另一种可能性是完全绕过PHP并使用Apache的authorization, authentication and access control工具。如果有必要,您甚至可以使用自己的身份验证/授权逻辑编写Apache模块。

答案 1 :(得分:1)

如果请求的文件不存在,您可以使用apach重写规则通过PHP路由请求。这样,如果您有/images/logo.png的请求,并且它存在,则apache将像平常一样提供服务。

这可能有用(我有点生疏,我讨厌mod_rewrite规则:P)

# Route requests through PHP if the file does not exist
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?query=$1&%{QUERY_STRING} [L]

<强>更新

您也可以定义哪些目录包含静态内容并将其排除

RewriteCond %{REQUEST_URI} !^/images/.* [AND]
RewriteCond %{REQUEST_URI} !^/css/.*
RewriteRule ^(.*)$ index.php?query=$1&%{QUERY_STRING} [L]

答案 2 :(得分:1)

  

事实证明,与让Apache做其事情相比,这是非常缓慢的。

我认为在这个低水平上加快操作还有很多工作要做。遗憾的是,没有简单的方法让PHP执行“登录”检查,然后将文件的实际服务传递给Apache。

据说像nginx这样的其他网络服务器在这个领域有更多的可能性。也许值得一试。

我前段时间问essentially the same question。也许反馈中有一些想法。我确实在我要求的项目中实现了接受答案的变体。

答案 3 :(得分:1)

添加Joe Hopfgartner的答案..

是的,输出缓冲可能会有所帮助,但是您应该检查客户端是否接受gzip压缩响应,如果确实如此,那么您也应该gzip缓冲区,响应将会很小,因此速度更快。 检查ob_ *函数上的php.net网站文档或谷歌如何gzip你的内容。

此外,

你应该调用set_time_limit(0)。如果你的脚本运行时间太长,它会中途转移中止,你会得到一个腐败的响应。

你也应该选择你所服务的内容,限制像css这样的文件可能无关紧要。

答案 4 :(得分:1)

首先关闭 - 您的代码只能提供应用程序/八位字节流和text / css,并且您正试图强制下载所有内容。

  

有人可以推荐一种方法来帮助我提高性能吗?

  • gzip如其他地方所述(但要选择它并缓存服务器端的压缩版本)

  • 使用mod_php或fastCGI优先于CGI

  • 使用操作码缓存,例如APC

  • 发送一些缓存信息(但如果您需要对所有内容进行身份验证,请包含Varies:Cookies标头)

  • 使用fpassthru而不是while循环

  • 使用反向代理

  • 在反向代理上进行身份验证(使用squid的url-rewriter简单)

还有其他方法可以在不使用重写的情况下解决问题(例如404处理程序,自动预置所有内容类型) - 您可以偶尔使用mod_rewrite获得内部递归问题。

答案 5 :(得分:0)

看起来你正试图在那里编写自己的小型Web服务器。所有你想要做的事情,Apache已经可以做得更高效了。我建议重新考虑你的方法。

答案 6 :(得分:0)

您的问题有更好的解决方案。

但你的答案是:使用输出缓冲 ob_start();冲洗(); 它会提高你的表现。

但是你有更好的方法:

对于经过身份验证的用户提供的链接包括当前时间戳和文件名/ id的哈希值,时间戳和秘密令牌。 maby喜欢这样。 $ link = $ timetamp。“/”。md5($ secret_token。$ timestamp。$ filename)。“/”。$ filename。

然后在重写映射中传递这些参数并使用服务器上的秘密令牌,传递的时间戳和文件重新创建哈希。如果哈希匹配,则返回文件的路径,如果超时或错误页面无效。

通过这种方式,您可以直接提供通过apache保护的文件。你还包括一个IP。

你还可以查看一下名为mod_secdownload的模块中存在类似taht的lighttpd。我认为还有apache的插件,我相信nginx

为您提供的一些链接:

http://redmine.lighttpd.net/wiki/1/Docs:ModSecDownload http://httpd.apache.org/docs/current/mod/mod_rewrite.html

答案 7 :(得分:0)

使用mod_mysql_auth进行apache。它几乎与你试图做的一样 以mod Auth MySQL Under Apache 2 and Debian为例。

我只用了一次,但它似乎对我有用。

答案 8 :(得分:0)

你能做什么:

  • 登录后,创建一个指向受保护资源的随机symblink(md5 ..)。
  • 将该路径传递给客户端
  • 在注销或超时时删除该symblink的一些逻辑。

这只是安全的一半,尤其是如果路径没有通过https。

OR:

  • 在apache中有一个受IP保护的目录。
  • 让PHP维护该目录条目,(添加和删除允许的IP)
  • (您需要“服务httpd reload”才能应用更改)