要求:
- 文件有权限验证。
- 文件大小可能会很大。
- 性能要好。
传统做法一:直接源始地址下载
就是直接给出文件的地址 http://www.xxx.com/file.rar,这种方式最直接,性能好,文件大小无限制,但没办法做权限验证。
传统做法二:PHP中转下载
例子代码:
header("Content-Type:application/octet-stream;charset=utf-8");
header('Content-Disposition: attachment; filename=file.rar');
readfile('/www/file.rar');
这种方式是将文件先读取到内存然后转发给用户,可以方便的做权限验证,但性能不好,会占用较大的内存,对大文件支持也不好,会超时。下载人数多服务器还可能会崩溃,因为每次都要把文件读到内存里。
当然有优化内存的方式:
header("Content-Type:application/octet-stream;charset=utf-8");
header('Content-Disposition: attachment; filename=file.rar');
$data = fopen('/www/file.rar', 'rb');
while (!feof($data)) {
echo @fread($data, 8192);
ob_flush();
flush();
}
fclose($data);
但治标不治本,大文件还是会超时,因为大文件下载耗时,超过了php最大执行时间。
当当当当~
新的做法:PHP与Nginx的X-Accel-Redirect配合方案
熟悉Nginx的朋友知道Nginx有个sendfile零拷贝技术,这个技术很厉害,静态文件下载不用占用内存,具体技术可参考https://37wiki.37wan.com/pages/viewpage.action?pageId=10650822
我们现在介绍的这个技术叫x-sendfile,它的原理是:服务端脚本程序(如php)负责处理用户请求,并构造响应头信息,然后文件下载的时候转发给Web服务器(如Nginx)处理,服务端脚本程序不再参与,由Web服务器把具体的文件直接发送给浏览器客户端,而且用sendfile零拷贝技术,避免了内存占用。
这个功能在Apache、Lighttpd叫x-sendfile,在Nginx上叫X-Accel-Redirect。
具体做法如下:
nginx配置文档
location /download
{
/* 声明为内部,只能nginx内部访问,用户是访问不了这个location的*/
internal;
root /www/download;
}
php代码:
header("Content-Type:application/octet-stream;charset=utf-8");
header('Content-Disposition: attachment; filename=file.rar');
header('X-Accel-Redirect: /download/file.rar');
这样Nginx会自动处理X-Accel-Redirect头要求的文件。
整个方案满足上面三点要求:
- 文件有权限验证。
- 文件超大不会导致php超时。
- 性能好,不占用内存。