设为首页 收藏本站
查看: 824|回复: 0

[经验分享] 使用Nginx的X-Sendfile机制提升PHP文件下载性能

[复制链接]

尚未签到

发表于 2016-12-28 09:57:28 | 显示全部楼层 |阅读模式
很多时候用户需要从网站下载文件,如果文件是可以通过一个固定链接公开获取的,那么我们只需将文件存放到 webroot 下的目录里就好。但大多数情况下,我们需要做权限控制,例如下载 PDF 账单,又例如下载网盘里的档案。这时,我们通常借助于脚本代码来实现,而这无疑会增加服务器的负担。


例如下面的代码:

<?php
    // 用户身份认证,若验证失败跳转
    authenticate();
    // 获取需要下载的文件,若文件不存在跳转
    $file = determine_file();
    // 读取文件内容
    $content=file_get_contents($file);
    // 发送合适的 HTTP 头
    header("Content-type: application/octet-stream");
    header('Content-Disposition: attachment; filename="' . basename($file) . '"');
    header("Content-Length: ". filesize($file));
    echo $content; // 或者 readfile($file);
?>一、这样做有什么问题?
这样做意味着我们的程序需要将文件内容从磁盘经过一个固定的 buffer 去循环读取到内存,再发送给前端 web 服务器,最后才到达用户。当需要下载的文件很大的时候,这种方式将消耗大量内存,甚至引发 php 进程超时或崩溃。Cache 也很头疼,更不用说中断重连的情况了。

一个理想的解决方式应该是,由 php 程序进行权限检查等逻辑判断,一切通过后,让前台的 web 服务器直接将文件发送给用户——像 Nginx 这样的前台更善于处理静态文件。这样一来 php 脚本就不会被 I/O 阻塞了。

二、什么是 X-Sendfile?
X-Sendfile 是一种将文件下载请求由后端应用转交给前端 web 服务器处理的机制,它可以消除后端程序既要读文件又要处理发送的压力,从而显著提高服务器效率,特别是处理大文件下载的情形下。

X-Sendfile 通过一个特定的 HTTP header 来实现:在 X-Sendfile 头中指定一个文件的地址来通告前端 web 服务器。当 web 服务器检测到后端发送的这个 header 后,它将忽略后端的其他输出,而使用自身的组件(包括 缓存头 和 断点重连 等优化)机制将文件发送给用户。

不过,在使用 X-Sendfile 之前,我们必须明白这并不是一个标准特性,在默认情况下它是被大多数 web 服务器禁用的。而不同的 web 服务器的实现也不一样,包括规定了不同的 X-Sendfile 头格式。如果配置失当,用户可能下载到 0 字节的文件。

使用 X-Sendfile 将允许下载非 web 目录中的文件(例如/root/),即使文件在 .htaccess 保护下禁止访问,也会被下载。

不同的 web 服务器实现了不同的 HTTP 头 sendfile 头 使用的 web 服务器
X-Sendfile Apache, Lighttpd v1.5, Cherokee
X-LIGHTTPD-send-file Lighttpd v1.4
X-Accel-Redirect Nginx, Cherokee

使用 X-SendFile 的缺点是你失去了对文件传输机制的控制。例如如果你希望在完成文件下载后执行某些操作,比如只允许用户下载文件一次,这个 X-Sendfile 是没法做到的,因为后台的 php 脚本并不知道下载是否成功。

三、怎样使用?
Apache 请参考mod_xsendfile模块。下面我介绍 Nginx 的用法。

Nginx 默认支持该特性,不需要加载额外的模块。只是实现有些不同,需要发送的 HTTP 头为 X-Accel-Redirect。另外,需要在配置文件中做以下设定

location /protected/ {
  internal;
  root   /some/path;
}internal 表示这个路径只能在 Nginx 内部访问,不能用浏览器直接访问防止未授权的下载。

于是 PHP 发送 X-Accel-Redirect 给 Nginx:

<?php
    $filePath = '/protected/iso.img';
    header('Content-type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . basename($file) . '"');
    //让Xsendfile发送文件
    header('X-Accel-Redirect: '.$filePath);
?>这样用户就会下载到 /some/path/protected/iso.img 这个路径下的文件。

如果你想发送的是 /some/path/iso.img 文件,那么 Nginx 配置应该是

location /protected/ {
  internal;
  alias   /some/path/; # 注意最后的斜杠


作者:lovelucy

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-320521-1-1.html 上篇帖子: [转]Nginx 0.5.33 + PHP 5.2.5(FastCGI)搭建胜过Apache 10倍的Web服务器(第2版) 下篇帖子: 使用nginx后,一些较长连接的请求超过60秒就会重复请求
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表