一般来说我们可以通过直接让URL指向一个位于Document Root下面的文件来引导用户下载文件
但是这样做就没办法做一些统计权限检查等等的工作于是很多时候我们采用让PHP来做转发为用户提供文件下载
$file = “/tmp/dummytargz”
header(“Contenttype application/octetstream”)
header(ContentDisposition attachment filename=“ basename($file) ”)
header(“ContentLength “ filesize($file))
readfile($file)
但是这个有一个问题就是如果文件是中文名的话有的用户可能下载后的文件名是乱码
于是我们做一下修改(参考
$file = “/tmp/中文名targz”
$filename = basename($file)
header(“Contenttype application/octetstream”)
//处理中文文件名
$ua = $_SERVER[“HTTP_USER_AGENT”]
$encoded_filename = urlencode($filename)
$encoded_filename = str_replace(“+” “%” $encoded_filename)
if (preg_match(“/MSIE/” $ua)) {
header(ContentDisposition attachment filename=“ $encoded_filename ”)
} else if (preg_match(“/Firefox/” $ua)) {
header(“ContentDisposition attachment filename*=”utf“ $filename ”)
} else {
header(ContentDisposition attachment filename=“ $filename ”)
}
header(ContentDisposition attachment filename=“ $filename ”)
header(“ContentLength “ filesize($file))
readfile($file)
输出的时候如果是Apache + PHP mod那么还需要发送到Apache的输出缓沖区最后才发送给用户而对于Nginx + fpm如果他们分开部署的话那还会带来额外的网络IO
恩现在看起来好多了不过还有一个问题那就是readfile虽然PHP的readfile尝试实现的尽量高效不占用PHP本身的内存但是实际上它还是需要采用MMAP(如果支持)或者是一个固定的buffer去循环读取文件直接输出
那么能不能不经过PHP这层直接让Webserver直接把文件发送给用户呢?
今天我看到了一个有意思的文章How I PHPXSendFile
我们可以使用Apache的module mod_xsendfile让Apache直接发送这个文件给用户
$file = “/tmp/中文名targz”
$filename = basename($file)
header(“Contenttype application/octetstream”)
//处理中文文件名
$ua = $_SERVER[“HTTP_USER_AGENT”]
$encoded_filename = urlencode($filename)
$encoded_filename = str_replace(“+” “%” $encoded_filename)
if (preg_match(“/MSIE/” $ua)) {
header(ContentDisposition attachment filename=“ $encoded_filename ”)
} else if (preg_match(“/Firefox/” $ua)) {
header(“ContentDisposition attachment filename*=”utf“ $filename ”)
} else {
header(ContentDisposition attachment filename=“ $filename ”)
}
header(ContentDisposition attachment filename=“ basename($file) ”)
//让Xsendfile发送文件
header(“XSendfile $file”)
Lighttpd和Nginx也有类似的模块大家有兴趣的可以去找找看XSendfile头将被Apache处理并且把响应的文件直接发送给Client