在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 连接mysql连接被重置的解决方法
Feb 15 PHP
php源码分析之DZX1.5加密解密函数authcode用法
Jun 17 PHP
ThinkPHP开发框架函数详解:C方法
Aug 14 PHP
学习php设计模式 php实现享元模式(flyweight)
Dec 07 PHP
WordPress中用于创建以及获取侧边栏的PHP函数讲解
Dec 29 PHP
什么是OneThink oneThink后台添加插件步骤
Apr 13 PHP
php数据访问之查询关键字
May 09 PHP
Yii+MYSQL锁表防止并发情况下重复数据的方法
Jul 14 PHP
php制作圆形用户头像的实例_自定义封装类源代码
Sep 18 PHP
PHP将英文数字转换为阿拉伯数字实例讲解
Jan 28 PHP
PHP实现发送微博消息功能完整示例
Dec 04 PHP
Laravel统一错误处理为JSON的方法介绍
Oct 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
php递归创建和删除文件夹的代码小结
2012/04/13 PHP
php缓冲 output_buffering的使用详解
2013/06/13 PHP
CI操作cookie的方法分析(基于helper类库)
2016/03/28 PHP
javascript字符串拼接的效率问题
2010/12/25 Javascript
通过百度地图获取公交线路的站点坐标的js代码
2012/05/11 Javascript
js 操作select和option常用代码整理
2012/12/13 Javascript
jquery遍历select元素(实例讲解)
2013/12/31 Javascript
JQuery做的一个简单的点灯游戏分享
2014/07/16 Javascript
js实现文本框中输入文字页面中div层同步获取文本框内容的方法
2015/03/03 Javascript
深入理解JavaScript编程中的同步与异步机制
2015/06/24 Javascript
jQuery结合CSS制作动态的下拉菜单
2015/10/27 Javascript
jQuery实现点击弹出背景变暗遮罩效果实例代码
2016/06/24 Javascript
JavaScript中的ajax功能的概念和示例详解
2016/10/17 Javascript
js+html制作简单日历的方法
2017/06/27 Javascript
JavaScript实现树的遍历算法示例【广度优先与深度优先】
2017/10/26 Javascript
layui.js实现的表单验证功能示例
2017/11/15 Javascript
解决layui轮播图有数据不显示的情况
2019/09/16 Javascript
javascript设计模式 ? 外观模式原理与用法实例分析
2020/04/15 Javascript
[48:48]2014 DOTA2国际邀请赛中国区预选赛 SPD-GAMING VS Dream TIME
2014/05/21 DOTA
python绘图库Matplotlib的安装
2014/07/03 Python
零基础写python爬虫之爬虫编写全记录
2014/11/06 Python
零基础写python爬虫之爬虫框架Scrapy安装配置
2014/11/06 Python
Python网络爬虫项目:内容提取器的定义
2016/10/25 Python
对Django url的几种使用方式详解
2019/08/06 Python
Python中BeautifuSoup库的用法使用详解
2019/11/15 Python
Python有参函数使用代码实例
2020/01/06 Python
详解python itertools功能
2020/02/07 Python
如何在vscode中安装python库的方法步骤
2021/01/06 Python
阿迪达斯意大利在线商店:adidas意大利
2016/09/19 全球购物
Nike台湾官方商店:Nike.com (TW)
2017/08/16 全球购物
蒙蒂塞罗商店:Monticello Shop
2018/11/25 全球购物
Seavenger官网:潜水服、浮潜、靴子和袜子
2020/03/05 全球购物
教师实习自我鉴定
2013/12/11 职场文书
地方课程教学计划
2015/01/19 职场文书
婚宴来宾致辞
2015/07/28 职场文书
Go语言应该什么情况使用指针
2021/07/25 Golang