关于PHP做文件下载效率优化和体验提升
Web开发中我们经常会遇到需要PHP处理要下载的文件,比如需要用户登陆才能下载文件,被下载的文件不允许直接访问,下载后的文件需要重命名等,通常我们可以使用下面的代码实现:
<?php authenticate(); //权限判断 // 读取文件内容 $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读取文件内容到内存,再发送到前端服务器,最后才能发送到客户端,还有各种网络超时和大量内存消耗,这将会是一个相当糟糕的设计。下面将介绍一个更直接高效的方式来实现。
X-Sendfile 是一种将文件下载请求由后端转交给前端服务器,由前端服务器直接处理文件下载请求的一种机制。它可以有效缓解后端服务器压力,将文件下载请求直接将由擅长处理静态文件请求的前端服务器如:Nginx、Lighthttpd等,极大地提高了文件下载请求效率。
X-Sendfile是通过一个非标准化的特定HTTP header实现的,后端服务器发送一个特定的HTTP header到前端服务器的时候,前端服务器从X-Sendfile得到请求的文件地址后,前端服务器将利用自身的功能机制将文件直接发送给用户。
X-Sendfile实际上是一种服务器内部的跳转机制,这种处理请求并不会将实际需要请求的文件地址暴露给用户,X-Sendfile做为一种非标准化的机制,不同的服务器的实现有所区别
SENDFILE 头 | 使用的 WEB 器 |
---|---|
X-Sendfile | Apache, Lighttpd v1.5, Cherokee |
X-LIGHTTPD-send-file | Lighttpd v1.4 |
X-Accel-Redirect | Nginx, Cherokee |
下面仅以Nginx为例讲解如何配置
Nginx默认是支持X-Sendfiler特性的,不需要额外加载模块,服务器端配置如下
location /down { internal; root /home/www; }
后端PHP程序需要使用header发送HTTP头X-Accel-Redirec,以下是download.php文件
<?php
$filePath = '/down/hello.pdf';
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');//文件
header('X-Accel-Redirect: '.$filePath);
?>
这样当访问 http://domain/download.php 的时候即下载 /home/www/down/hello.pdf文件
如果用户想下载 /home/www/hello.pdf 文件的话,需要使用nginx的alias关键字,具体配置如下
location /down { internal; alias /home/www; }
另外有时候在部分浏览器上面会出现直接在浏览器中打开PDF文件,而不是另存为文件的问题,只需要设置一下服务器响应的MIME,可通过Nginx或者PHP实现
Nginx配置实现:
if ($request_filename ~* ^.*?\.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx)$){ add_header Content-Type 'application/octet-stream'; }
在location配置项中增加以上配置,通过正则表达式匹配实现对特定后缀的文件实现浏览器打开『另存为』窗口
PHP实现:
header(
"Content-type: application/octet-stream"
);
在文件输出中增加特定的Header头,注意在页面跳转之前输出以上头无效,包括在header(“Location http://domain/hello.pdf”) 语句,而对于上面X-Accel-Redirect实现的内部跳转中则可以使用。
参考资料
https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-redirect
http://www.laruence.com/2012/05/02/2613.html