在PHP中使用X-SendFile头让文件下载更快


Posted in PHP onJune 01, 2014

一般来说, 我们可以通过直接让URL指向一个位于Document Root下面的文件, 来引导用户下载文件.

但是, 这样做, 就没办法做一些统计, 权限检查, 等等的工作. 于是, 很多时候, 我们采用让PHP来做转发, 为用户提供文件下载.

<?php
    $file = "/tmp/dummy.tar.gz";
    header("Content-type: application/octet-stream");
    header('Content-Disposition: attachment; filename="' . basename($file) . '"');
    header("Content-Length: ". filesize($file));
    readfile($file);

但是这个有一个问题, 就是如果文件是中文名的话, 有的用户可能下载后的文件名是乱码.

于是, 我们做一下修改:

<?php
    $file = "/tmp/中文名.tar.gz";    $filename = basename($file);
    header("Content-type: application/octet-stream");
    //处理中文文件名
    $ua = $_SERVER["HTTP_USER_AGENT"];
    $encoded_filename = rawurlencode($filename);
    if (preg_match("/MSIE/", $ua)) {
     header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
    } else if (preg_match("/Firefox/", $ua)) {
     header("Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"');
    } else {
     header('Content-Disposition: attachment; filename="' . $filename . '"');
    }
    header("Content-Length: ". filesize($file));
    readfile($file);

恩, 现在看起来好多了, 不过还有一个问题, 那就是readfile, 虽然PHP的readfile尝试实现的尽量高效, 不占用PHP本身的内存, 但是实际上它还是需要采用MMAP(如果支持), 或者是一个固定的buffer去循环读取文件, 直接输出.

输出的时候, 如果是Apache + PHP mod, 那么还需要发送到Apache的输出缓冲区. 最后才发送给用户. 而对于Nginx + fpm如果他们分开部署的话, 那还会带来额外的网络IO.

那么, 能不能不经过PHP这层, 直接让Webserver直接把文件发送给用户呢?

今天, 我看到了一个有意思的文章: How I PHP: X-SendFile.

我们可以使用Apache的module mod_xsendfile, 让Apache直接发送这个文件给用户:

<?php
    $file = "/tmp/中文名.tar.gz";    $filename = basename($file);
    header("Content-type: application/octet-stream");
    //处理中文文件名
    $ua = $_SERVER["HTTP_USER_AGENT"];
    $encoded_filename = rawurlencode($filename);
    if (preg_match("/MSIE/", $ua)) {
     header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
    } else if (preg_match("/Firefox/", $ua)) {
     header("Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"');
    } else {
     header('Content-Disposition: attachment; filename="' . $filename . '"');
    }
    //让Xsendfile发送文件
    header("X-Sendfile: $file");

X-Sendfile头将被Apache处理, 并且把响应的文件直接发送给Client.

Lighttpd和Nginx也有类似的模块, 大家有兴趣的可以去找找看

PHP 相关文章推荐
十天学会php(2)
Oct 09 PHP
如何利用php+mysql保存和输出文件
Oct 09 PHP
超级简单的发送邮件程序
Oct 09 PHP
PHP+DBM的同学录程序(2)
Oct 09 PHP
一个用php实现的获取URL信息的类
Jan 02 PHP
php5.5新数组函数array_column使用
Jul 08 PHP
php生成shtml类用法实例
Dec 09 PHP
php从完整文件路径中分离文件目录和文件名的方法
Mar 13 PHP
php获取指定范围内最接近数的方法
Jun 02 PHP
php生成高清缩略图实例详解
Dec 07 PHP
Joomla简单判断用户是否登录的方法
May 04 PHP
php+ajax实现异步上传文件或图片功能
Jul 18 PHP
PHP is_subclass_of函数的一个BUG和解决方法
Jun 01 #PHP
PHP中数组的分组排序实例
Jun 01 #PHP
php_screw安装使用教程(另一个PHP代码加密实现)
May 29 #PHP
PHP Curl出现403错误的解决办法
May 29 #PHP
PHP的foreach中使用引用时需要注意的一个问题和解决方法
May 29 #PHP
神盾加密解密教程(一)PHP变量可用字符
May 28 #PHP
CI框架开发新浪微博登录接口源码完整版
May 28 #PHP
You might like
使用Limit参数优化MySQL查询的方法
2008/11/12 PHP
用php随机生成福彩双色球号码的2种方法
2013/02/04 PHP
使用 laravel sms 构建短信验证码发送校验功能
2017/11/06 PHP
PHP堆栈调试操作简单示例
2018/06/15 PHP
Code: write(s,d) 输出连续字符串
2007/08/19 Javascript
该如何加载google-analytics(或其他第三方)的JS
2010/05/13 Javascript
Js 随机数产生6位数字
2010/05/13 Javascript
addEventListener和attachEvent二者绑定的执行函数中的this不相同
2012/12/09 Javascript
jquery按回车提交数据的代码示例
2013/11/05 Javascript
让IE8浏览器支持function.bind()方法
2014/10/16 Javascript
javascript父子页面通讯实例详解
2015/07/17 Javascript
js中利用tagname和id获取元素的方法
2016/01/03 Javascript
全面解析JavaScript中的valueOf与toString方法(推荐)
2016/06/14 Javascript
js学习阶段总结(必看篇)
2016/06/16 Javascript
jQuery实现打开页面渐现效果示例
2016/07/27 Javascript
jQuery实现为LI列表前3行设置样式的方法【2种方法】
2016/09/04 Javascript
浅谈JS中String()与 .toString()的区别
2016/10/20 Javascript
15个非常实用的JavaScript代码片段
2016/12/18 Javascript
原生js获取浏览器窗口及元素宽高常用方法集合
2017/01/18 Javascript
从零开始学习Node.js系列教程之设置HTTP头的方法示例
2017/04/13 Javascript
基于LayUI实现前端分页功能的方法
2017/07/22 Javascript
vue如何通过id从列表页跳转到对应的详情页
2018/05/01 Javascript
jQuery实现常见的隐藏与展示列表效果示例
2018/06/04 jQuery
vue环形进度条组件实例应用
2018/10/10 Javascript
对layui中table组件工具栏的使用详解
2019/09/19 Javascript
Python 实现大整数乘法算法的示例代码
2019/09/17 Python
Python3的unicode编码转换成中文的问题及解决方案
2019/12/10 Python
浅谈图像处理中掩膜(mask)的意义
2020/02/19 Python
python GUI库图形界面开发之PyQt5 Qt Designer工具(Qt设计师)详细使用方法及Designer ui文件转py文件方法
2020/02/26 Python
phonegap常用事件总结(必看篇)
2017/03/31 HTML / CSS
Nili Lotan官网:Nili Lotan同名品牌
2018/01/07 全球购物
资产经营总监岗位职责范文
2013/12/01 职场文书
打架检讨书400字
2014/01/17 职场文书
人事助理自荐信
2014/02/02 职场文书
小学中队长竞选稿
2015/11/20 职场文书
《迟到》教学反思
2016/02/24 职场文书