为了对文件下载进行控制,例如:计费、统计、权限控制等,通常由PHP提供文件的下载,即下载是通过 类似 download.php?id=xxx的方式进行,而不是在 document root 下直接 文件的下载地址。但是由PHP处理文件的下载,没有WEB服务器直接负责静态文件的下载来得高效。如果权限控制、计费等附加功能由PHP处理,而文件下载过程则交由WEB服务器直接处理,速度就可以得到很大的提升。
X-Sendfile机制可实现在PHP文件中把文件的下载功能转交给WEB服务器处理的功能。apache, nginx均支持此种机制,apache中由第三方模块 mod_xsendfile 模块提供该功能,nginx中则自带X-Sendfile支持。
apache安装配置X-Sendfile过程如下:
a. 安装
1 2 3 4 5
| wget https://tn123.org/mod_xsendfile/mod_xsendfile-0.12.tar.bz2 tar xvf mod_xsendfile-0.12.tar.bz2 cd mod_xsendfile-0.12 apxs -cia mod_xsendfile.c
|
b.配置
在 httpd.conf 中添加配置:
1 2 3
| <IfModule xsendfile_module> XSendFile on </IfModule>
|
在VirtualHost 里添加 XSendPath 指令,例如:
1 2 3 4
| <VirtualHost *> ServerName someserver XSendFilePath /var/www/protected </VirtualHost>
|
/var/www/protected 为需要支持 X-Sendfile下载的目录
c. 使用 X-Sendfile 功能
在需要用到 X-Sendfile的PHP文件里,添加如下语句:
header('X-Sendfile: /file/download/path');
/file/download/path 替换成实际要下载的文件的上级目录的绝对路径
nginx 安装配置 X-Sendfile 过程如下:
a. 配置
nginx.conf 的 Server{} 里添加如下配置项:
1 2 3 4
| location /protected { internal; alias /file/download/pat; }
|
/protected 只要跟 document root 中的文件目录不相同,可任意更改
/file/download/path 替换成实际要下载的文件的上级目录的绝对路径
b. 使用 X-Sendfile 功能
在需要用到的PHP文件里,添加:
header('X-Accel-Redirect: /protected/filename');
/protected/filename 的 filename
以下是同时支持 apache、nginx两种WEB服务器的X-Sendfile下载代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?php header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); $ua = $_SERVER["HTTP_USER_AGENT"]; if (preg_match('/MSIE/', $ua)) { header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"'); } elseif (preg_match("/Firefox/", $ua)) { header('Content-Disposition: attachment; filename*="utf8\'\'' . $filename . '"'); } else { header('Content-Disposition: attachment; filename="' . $filename . '"'); } header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); header('Content-Length: ' . filesize($filePath)); if (strpos($_SERVER["SERVER_SOFTWARE"], 'Apache') !== false) { header('X-Sendfile: ' . $filePath); } elseif (strpos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false) { // 使用 nginx 服务器时,则把 文件下载交给 nginx 处理,这样效率高些 header('X-Accel-Redirect: '. '/protected/' . $filename); } else { set_time_limit(300); // 避免下载超时 ob_end_clean(); // 避免大文件导致超过 memory_limit 限制 readfile($filePath); } ?>
|
使用和未使用X-Sendfile的对比:
下载速度比较 (使用 wget,在 /dev/shm/ 目录中,下载494M的视频文件)
apache
- 未使用X-Sendfile: 44.9MB/s in 11s
- 使用X-Sendfile: 90.0MB/s in 6.1s
nginx
- 未使用X-Sendfile: 96.9MB/s in 5.1s
- 使用X-Sendfile: 1235MB/s in 0.4s
由上测试结果可知,使用X-Sendfile可以大大地提高php下文件的下载速度