在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 相关文章推荐
ajax php 实现写入数据库
Sep 02 PHP
PHP,ASP.JAVA,JAVA代码格式化工具整理
Jun 15 PHP
php设计模式 Visitor 访问者模式
Jun 28 PHP
单一index.php实现PHP任意层级文件夹遍历(Zjmainstay原创)
Jul 31 PHP
easyui的tabs update正确用法分享
Mar 21 PHP
php使用正则表达式提取字符串中尖括号、小括号、中括号、大括号中的字符串
Apr 05 PHP
Yii框架中 find findAll 查找出制定的字段的方法对比
Sep 10 PHP
PHP获取当前页面URL函数实例
Oct 22 PHP
php查询mysql数据库并将结果保存到数组的方法
Mar 18 PHP
PHP根据树的前序遍历和中序遍历构造树并输出后序遍历的方法
Nov 10 PHP
PHP切割汉字的常用方法实例总结
Apr 27 PHP
php判断/计算闰年的方法小结【三种方法】
Jul 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
PHP 使用pcntl和libevent 实现Timer功能
2013/10/27 PHP
php计算两个文件相对路径的方法
2015/03/14 PHP
PHP curl伪造IP地址和header信息代码实例
2015/04/27 PHP
php版微信支付api.mch.weixin.qq.com域名解析慢原因与解决方法
2016/10/12 PHP
PHP Swoole异步读取、写入文件操作示例
2019/10/24 PHP
JavaScript 替换Html标签实现代码
2009/10/14 Javascript
jQuery 验证插件 Web前端设计模式(asp.net)
2010/10/17 Javascript
兼容最新firefox、chrome和IE的javascript图片预览实现代码
2014/08/08 Javascript
nodejs npm package.json中文文档
2014/09/04 NodeJs
node.js中的url.resolve方法使用说明
2014/12/10 Javascript
jquery validate和jquery form 插件组合实现验证表单后AJAX提交
2015/08/26 Javascript
javascript数据结构之双链表插入排序实例详解
2015/11/25 Javascript
Js实现简单的小球运动特效
2016/02/18 Javascript
浅析JS操作DOM的一些常用方法
2016/05/13 Javascript
探讨AngularJs中ui.route的简单应用
2016/11/16 Javascript
vue2.0使用Sortable.js实现的拖拽功能示例
2017/02/21 Javascript
每个 JavaScript 工程师都应懂的33个概念
2018/10/22 Javascript
vue实现在线学生录入系统
2020/05/30 Javascript
vue+elementUI 实现内容区域高度自适应的示例
2020/09/26 Javascript
python抓取京东价格分析京东商品价格走势
2014/01/09 Python
Python爬虫基础之XPath语法与lxml库的用法详解
2018/09/13 Python
对pycharm 修改程序运行所需内存详解
2018/12/03 Python
关于django 1.10 CSRF验证失败的解决方法
2019/08/31 Python
python实现把二维列表变为一维列表的方法分析
2019/10/08 Python
python标识符命名规范原理解析
2020/01/10 Python
python实现逢七拍腿小游戏的思路详解
2020/05/26 Python
matplotlib基础绘图命令之errorbar的使用
2020/08/13 Python
K近邻法(KNN)相关知识总结以及如何用python实现
2021/01/28 Python
基于HTML5代码实现折叠菜单附源码下载
2015/11/27 HTML / CSS
Canvas高级路径操作之拖拽对象的实现
2019/08/05 HTML / CSS
阿迪达斯荷兰官方网站:adidas荷兰
2018/03/16 全球购物
命名空间(namespace)和程序集(Assembly)有什么区别
2015/09/25 面试题
外企办公室竞聘演讲稿
2013/12/29 职场文书
小学生差生评语
2014/12/29 职场文书
考试没考好检讨书
2015/05/06 职场文书
HTML速写之Emmet语法规则的实现
2021/04/07 HTML / CSS