关于X-Sendfile
X-Sendfile 是一种将文件下载请求由后端应用转交给前端 web 服务器处理的机制,它可以消除后端程序既要读文件又要处理发送的压力,从而显著提高服务器效率,特别是处理大文件下载的情形下。
摘自CSDN
在没有开启X-Sendfile之前,我们在可道云下载一次文件要历经:PHP收到请求->检查权限->判断存在并找到文件->告知PHP读入内存->系统读文件到内存->系统给PHP->PHP移交给前端Web服务器->下载
这样的过程可能计算机是一瞬间,但是这样在下载大文件或者大并发下载时,会导致文件被多次/大量读入内存,不仅非常消耗内存还有一定可能导致PHP-FPM的崩溃。
如果此时我们利用X-Sendfile来下载文件,这个过程会变成:PHP收到请求->检查权限->判断存在并找到文件->告诉前端文件服务器读取文件->系统读文件到内存->下载
开始设置
参考可道云的开发文档,它其实是内置提供了X-Sendfile的支持,与此同时你的WEB服务器也要能对X-sendfile提供支持。
Nginx与Light httpd默认提供,Apache需要安装额外模块,请参考此文
本教程以Nginx为例进行设置。
一些小修改
可道云截止 version 4.39 还未对其代码内写错了X-sendfile头部的问题修复,您可能需要进行一些修改。
在#KOD/app/function/file.function.php
的L1042
左右,关于输出X-Sendfile头部的代码:
$server = strtolower($_SERVER['SERVER_SOFTWARE']);
if($server && $GLOBALS['config']['settings']['httpSendFile']){
if(strstr($server,'nginx')){//nginx
header('X-Sendfile: '.$file);
}else if(strstr($server,'apache')){ //apache
header("X-Accel-Redirect: ".$file);
}else if(strstr($server,'http')){//light http
header( "X-LIGHTTPD-send-file: " . $file);
}
return;
}
其中,nginx
与apache
的头部写反了..
将他们对换位置:
$server = strtolower($_SERVER['SERVER_SOFTWARE']);
if($server && $GLOBALS['config']['settings']['httpSendFile']){
if(strstr($server,'nginx')){//nginx
header("X-Accel-Redirect: ".$file);
}else if(strstr($server,'apache')){ //apache
header('X-Sendfile: '.$file);
}else if(strstr($server,'http')){//light http
header( "X-LIGHTTPD-send-file: " . $file);
}
return;
}
随后即可正常工作。
对Nginx进行配置
我们假定我们的网站目录为/www/wwwroot/web/
我们在此搭建了可道云,现在我需要下载/www/wwwroot/web/data/1.txt
或者是/www/1.txt
这时,下载文件时输出头部应该为:X-Accel-Redirect: /www/wwwroot/web/data/1.txt
那我们访问的网页路径其实是/www
但如果头部输出的是X-Accel-Redirect: /data/1.txt
那我们访问的网页路径就是/data
这样让你理解接下来的配置为什么要那样设置。
打开对应站点的配置,增添一段在合适的位置(推荐在SSL相关配置后添加)
location /www {
root /;
internal;
}
熟悉nginx配置的文件的人应该能看懂了,我们头部指示下载的文件是在www
目录,而www
目录是在/
根目录下,internal
代表该目录仅能在nginx内部处理,不允许外部直接访问。
如果我需要仅data目录能被下载
如果是情况二,也就是X-Accel-Redirect
为/data/1.txt
但其目录其实在/www/wwwroot/web/data
也就是可道云目前的结构,那么配置应该写为:
location /data {
root /www/wwwroot/web;
internal;
}
但是,需要另外对可道云进行修改,使可道云请求下载的header内的地址变为/data/1.txt
否则会无法找到文件,如果按照上面的配置修改了nginx但是可道云没有做对应修改,那么nginx收到的请求下载的路径应该是:/www/wwwroot/web/www/wwwroot/web/data/1.txt
这显然不是我们想要的。
我们需要在刚才修正header的地方加以修改:
//调用webserver下载
$server = strtolower($_SERVER['SERVER_SOFTWARE']);
$x_file = str_replace(BASIC_PATH,"",$file);
if($server && $GLOBALS['config']['settings']['httpSendFile']){
if(strstr($server,'nginx')){//nginx
header("X-Accel-Redirect: /".$x_file);
}else if(strstr($server,'apache')){ //apache
header('X-Sendfile: '.$file);
}else if(strstr($server,'http')){//light http
header( "X-LIGHTTPD-send-file: " . $file);
}
return;
}
在此,增加了变量x_file
作为处理变量,目的是将文件地址中删掉可道云目录的部分,这里利用了KOD内置定义的目录BASIC_PATH
按照我们上面的配置也就是/www/wwwroot/web/
然后使用了字符替换函数替换为空,并且在请求头时补全一个/
使路径完整。
相关已定义的变量可以在#KOD/config/config.php
找到。
这就做之后就可以只允许可道云下载data
目录下的文件了,且可道云根目录及上级目录则无法下载文件。
在可道云开启httpSendFile
在可道云目录下的config
创建新文件setting_user.php
写入
<?php
$GLOBALS['config']['settings']['httpSendFile'] = true;
也可以附加到现有配置文件(如果有)
此时,试着下载一个文件吧。
后话
利用这个特性,php能够下载web目录之外的内容,但是nginx内配置x-sendfile
的目录最多到第二层,也就是根目录的下一层而无法直接从根目录开始访问,这样可以避免关键文件被下载。
同时,不知道是不是我个人的原因,php文件有可能会被其他配置所影响,导致无法下载.php
文件。
对于个人站点我推荐将配置设置到/www
这样可以方便调试其他站点,公有站点请最好设置到/data
以确保安全。