在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生成缩略图的类代码
Oct 02 PHP
mysql下创建字段并设置主键的php代码
May 16 PHP
PHP之APC缓存详细介绍 apc模块安装
Jan 13 PHP
PHP独立Session数据库存储操作类分享
Jun 11 PHP
浅谈PHP中JSON数据操作
Jul 01 PHP
Thinkphp框架开发移动端接口(2)
Aug 18 PHP
Yii2实现自定义独立验证器的方法
May 05 PHP
PHP+MariaDB数据库操作基本技巧备忘总结
May 21 PHP
使用PHP访问RabbitMQ消息队列的方法示例
Jun 06 PHP
php封装的pdo数据库操作工具类与用法示例
May 08 PHP
PHP实现数组根据某个字段进行水平合并,横向合并案例分析
Oct 08 PHP
Laravel 5+ .env环境配置文件详解
Apr 06 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
用ADODB来让PHP操作ACCESS数据库的方法
2006/12/31 PHP
php笔记之:有规律大文件的读取与写入的分析
2013/04/26 PHP
php学习笔记之面向对象
2014/11/08 PHP
微信支付开发订单查询实例
2016/07/12 PHP
JavaScript生成GUID的多种算法小结
2013/08/18 Javascript
zTree插件之多选下拉菜单实例代码
2013/11/06 Javascript
js定时器(执行一次、重复执行)
2014/03/07 Javascript
js函数调用的方式
2014/05/06 Javascript
jquery 显示*天*时*分*秒实现时间计时器
2014/05/07 Javascript
jquery实现textarea输入框限制字数的方法
2015/01/15 Javascript
基于jQuery实现网页进度显示插件
2015/03/04 Javascript
简述Matlab中size()函数的用法
2016/03/20 Javascript
聊一聊JavaScript作用域和作用域链
2016/05/03 Javascript
Bootstrap CSS布局之列表
2016/12/15 Javascript
javascript判断元素存在和判断元素存在于实时的dom中的方法
2017/01/17 Javascript
jQuery DOM节点的遍历方法小结
2017/08/15 jQuery
vue判断input输入内容全是空格的方法
2018/03/02 Javascript
JS实现的判断方法、变量是否存在功能示例
2020/03/28 Javascript
vue框架制作购物车小球动画效果实例代码
2019/09/26 Javascript
ant design vue嵌套表格及表格内部编辑的用法说明
2020/10/28 Javascript
[51:17]Mski vs VGJ.S Supermajor小组赛C组 BO3 第三场 6.3
2018/06/04 DOTA
Python中支持向量机SVM的使用方法详解
2017/12/26 Python
Python数据可视化之画图
2019/01/15 Python
Python微信操控itchat的方法
2019/05/31 Python
使用PyQt4 设置TextEdit背景的方法
2019/06/14 Python
Python适配器模式代码实现解析
2019/08/02 Python
django 扩展user用户字段inlines方式
2020/03/30 Python
国际化的太阳镜及太阳镜配件零售商:Sunglass Hut
2016/07/26 全球购物
怎样声明接口
2014/09/19 面试题
Linux的主要特性
2016/09/03 面试题
电子装配专业毕业生求职信
2014/04/23 职场文书
新书发布会策划方案
2014/06/09 职场文书
运动会演讲稿300字
2014/08/25 职场文书
2014年企业党支部工作总结
2014/12/04 职场文书
预备党员考察意见范文
2015/06/01 职场文书
军训结束新闻稿
2015/07/17 职场文书