如何检测 PHP 中的 X-Accel-Redirect (Nginx) / X-Sendfile (Apache) 支持?

Posted

技术标签:

【中文标题】如何检测 PHP 中的 X-Accel-Redirect (Nginx) / X-Sendfile (Apache) 支持?【英文标题】:How to detect X-Accel-Redirect (Nginx) / X-Sendfile (Apache) support in PHP? 【发布时间】:2011-04-30 15:38:54 【问题描述】:

关于应用

我正在使用 php 开发电子商务应用程序。为了保证 URL 的安全,产品下载链接保留在 PHP 后面。有一个文件,比如 download.php,它通过 GET 接受少量参数并根据数据库验证它们。如果一切顺利,它将使用 PHP 中的 readfile() 函数提供文件。

关于问题

现在问题来了,当传递给 readfile() 的文件大于 php.ini 中设置的内存限制时 由于许多用户将在共享主机上使用此应用程序,因此我们无法更改 php.ini 设置。

在我们努力寻找解决方法的过程中,我首先认为我们可以在 while 循环中进行 fread() 调用,但这似乎会带来问题,并在此处突出显示Downloading large files reliably in PHP

所以我最好的选择是检测/检查服务器是否支持 X-Accel-Redirect(如果是 Nginx)/X-Sendfile(如果是 Apache)

如果服务器支持 X-Accel-Redirect / X-Sendfile,我可以使用它们,并且在 else 块中我可以让系统管理员了解 php.ini 强制执行的内存限制

理想情况下,我希望尽可能使用 X-Accel-Redirect / X-Sendfile 等服务器端支持,如果这不起作用 - 我希望有一个备用代码来读取没有 readfile() 的文件。

我还不确定 while 循环中的 readfile() 和 fread() 有何不同,但似乎 while 循环会再次产生问题,正如 Downloading large files reliably in PHP 中所建议的那样

希望能得到一些帮助、建议、代码、指导。

感谢阅读。

【问题讨论】:

【参考方案1】:

要检测是否安装了 mod_xsendfile apache 模块,您可以尝试以下代码:

if function_exists('apache_get_modules') 
      && in_array('mod_xsendfile', apache_get_modules())  
  header("X-Sendfile"); 

但此代码只是检查模块是否仅安装,如果安装但配置错误可能会导致错误

另一种可能的方法是通过 Apache 的 .htaccess 设置服务器范围的变量:

<IfModule mod_xsendfile.c>
  <Files *.php>
    XSendFile On
    XSendFileAllowAbove On
    SetEnv MOD_X_SENDFILE_ENABLED 1
  </Files>
</IfModule>

并从 php 代码中检查它:

if ($_SERVER['MOD_X_SENDFILE_ENABLED']) 
  Header(...)

nginx 的常见想法是相同的 - 只需通过 HTTP-header 或 CGI/FastCGI 变量将状态变量的值传递给后端。

【讨论】:

感谢您的帮助。上面的代码在这两种情况下都可以工作 - 当 php 用作 Apache'e mod_php 或 fastcgi/fcgi 时? 另外,我猜在 Nginx 上,对 X-Accel-Redirect 的支持是核心的一部分,所以我只需要以某种方式检测 (webserver == "nginx") !对吗? 顺便说一句,上面的代码 sn-p 帮助很大。所以谢谢你。 :-) 第一个代码 sn-p 仅适用于 mod_php,但第二个代码适用于 mod_php 和 CGI​​/FastCGI 模式。 关于 nginx - 看起来你是对的,但你的 cgi/fastcgi 层对前端服务器软件一无所知,不像你会通知它(使用 CGI 变量或 HTTP-header )【参考方案2】:

readfile 不会占用大量内存。它打开文件,读取一小部分,将该部分写入浏览器,然后将内存重新用于下一次读取。这与在 while 循环中使用 fread+echo 相同。您不会受到内存限制的限制,但您会受到 max_execution_time 等的限制。

如果您想使用 Web 服务器提供的 X-Accel-Redirect 支持(或类似支持),请发送这样的标头(用于 Nginx):

header('X-Accel-Redirect: /path/to/file');

您的应用程序无法知道服务器是否支持此功能。您将需要提供一个配置选项,以便您的软件的管理员/安装程序可以手动提供此类信息。

【讨论】:

感谢您对 readfile 的深入了解。如果 readfile() 在小内存中工作,为什么当我们打开大文件时它会抛出“内存不足”错误。一旦我们更改了 php.ini 设置,同样的错误也消失了。 readfile() 也是我的最后一行,所以我确信在 readfile 之后,我的应用程序不会进一步处理数据。 还有一个 - 如果我写了header('X-Accel-Redirect: /path/to/file'); 并且底层服务器没有正确配置,会发生什么? header() 调用会被忽略吗?我会收到一些错误消息吗? rahul286,有人在php.net/readfile 上解释了 cmets 中可能存在的内存问题:“这是因为您打开了输出缓冲。只需在调用 Readfile() 之前立即关闭输出缓冲。使用一些东西比如 ob_end_flush()。” 此外,如果您的 Web 服务器不支持 X-Accel-Redirect(或类似的),标头将被发送到 Web 浏览器,浏览器将忽略它。您不会收到任何错误消息。 Emil,我注意到 output_buffering 问题。但是由于我的代码是作为 wordpress 插件的包,并且我还没有启动输出缓冲,所以我选择在循环内使用 flush() 调用继续我的 while 循环。有效。 :-)【参考方案3】:

您可以在服务器上设置一个环境变量来控制正确的服务器标头名称(可能由操作团队或负责编写/定义服务器环境的任何人设置)。这将适用于任何支持基于 Header 的文件服务的堆栈,因为控制该堆栈的人可以做出决定,而您只需从环境中读取标题。

<?php

/**
 * Method to delegate sending file to webserver
 *
 * Uses environment variable `SERVER_ACCEL_HEADER` to define the specific
 * server header to use to send files from server layer rather than 
 * application layer
 *
 * @param  string $path path to file that should exist
 * @param  bool $die if script should terminate after setting header
 * @return void returns nothing
 */
function sendPathAccel($path, $die=true) 
    $accelHeader = getenv('SERVER_ACCEL_HEADER');
    header("$accelHeader: $path");
    if($die)  die(); 

警告:需要注意的是,这不是新手操作,所以要小心。如果负责服务器环境的人不设置它,使用它可能会出现很多问题,它可能无法正常工作或可能导致错误,但它简单、快速,我想不出改变它的理由。

【讨论】:

以上是关于如何检测 PHP 中的 X-Accel-Redirect (Nginx) / X-Sendfile (Apache) 支持?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 php 中的用户代理字符串中检测浏览器欺骗和机器人

php中的ipad方向检测?

如何检测 PHP 中的 X-Accel-Redirect (Nginx) / X-Sendfile (Apache) 支持?

如何在php中检测歌曲的BPM [关闭]

如何在 PHP 中检测、删除和重定向带有 # 的 URL?

如何通过 php/ajax/javascript 动态检测蜂窝运营商? [关闭]