分享一个超好用的php header下载函数


Posted in PHP onJanuary 31, 2014
<?php
/**
 * 发送文件
 *
 * @author: legend(legendsky@hotmail.com)
 * @link: http://www.ugia.cn/?p=109
 * @description: send file to client
 * @version: 1.0
 *
 * @param string   $fileName      文件名称或路径
 * @param string   $fancyName     自定义的文件名,为空则使用filename
 * @param boolean  $forceDownload 是否强制下载
 * @param integer  $speedLimit    速度限制,单位为字节,0为不限制,不支持windows服务器
 * @param string   $$contentType  文件类型,默认为application/octet-stream
 *
 * @return boolean
 */
function sendFile($fileName, $fancyName = '', $forceDownload = true, $speedLimit = 0, $contentType = '')
{
    if (!is_readable($fileName))
    {
        header("HTTP/1.1 404 Not Found");
        return false;
    }
    $fileStat = stat($fileName);
    $lastModified = $fileStat['mtime'];
    $md5 = md5($fileStat['mtime'] .'='. $fileStat['ino'] .'='. $fileStat['size']);
    $etag = '"' . $md5 . '-' . crc32($md5) . '"';
    header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT');
    header("ETag: $etag");
    if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lastModified)
    {
        header("HTTP/1.1 304 Not Modified");
        return true;
    }
    if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) < $lastModified)
    {
        header("HTTP/1.1 304 Not Modified");
        return true;
    }
    if (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&  $_SERVER['HTTP_IF_NONE_MATCH'] == $etag)
    {
        header("HTTP/1.1 304 Not Modified");
        return true;
    }
    if ($fancyName == '')
    {
        $fancyName = basename($fileName);
    }
    if ($contentType == '')
    {
        $contentType = 'application/octet-stream';
    }
    $fileSize = $fileStat['size'];   
    $contentLength = $fileSize;
    $isPartial = false;
    if (isset($_SERVER['HTTP_RANGE']))
    {
        if (preg_match('/^bytes=(d*)-(d*)$/', $_SERVER['HTTP_RANGE'], $matches))
        {    
            $startPos = $matches[1];
            $endPos = $matches[2];
            if ($startPos == '' && $endPos == '')
            {
                return false;
            }
            if ($startPos == '')
            {
                $startPos = $fileSize - $endPos;
                $endPos = $fileSize - 1;
            }
            else if ($endPos == '')
            {
                $endPos = $fileSize - 1;
            }
            $startPos = $startPos < 0 ? 0 : $startPos;
            $endPos = $endPos > $fileSize - 1 ? $fileSize - 1 : $endPos;
            $length = $endPos - $startPos + 1;
            if ($length < 0)
            {
                return false;
            }
            $contentLength = $length;
            $isPartial = true;
        }
    }
    // send headers
    if ($isPartial)
    {
        header('HTTP/1.1 206 Partial Content');
        header("Content-Range: bytes $startPos-$endPos/$fileSize");
    }
    else
    {
        header("HTTP/1.1 200 OK");
        $startPos = 0;
        $endPos = $contentLength - 1;
    }
    header('Pragma: cache');
    header('Cache-Control: public, must-revalidate, max-age=0');
    header('Accept-Ranges: bytes');
    header('Content-type: ' . $contentType);
    header('Content-Length: ' . $contentLength);
    if ($forceDownload)
    {
        header('Content-Disposition: attachment; filename="' . rawurlencode($fancyName). '"');//汉字自动转为URL编码
  header('Content-Disposition: attachment; filename="' . $fancyName. '"');
    }
    header("Content-Transfer-Encoding: binary");
    $bufferSize = 2048;
    if ($speedLimit != 0)
    {
        $packetTime = floor($bufferSize * 1000000 / $speedLimit);
    }
    $bytesSent = 0;
    $fp = fopen($fileName, "rb");
    fseek($fp, $startPos);
    //fpassthru($fp);
    while ($bytesSent < $contentLength && !feof($fp) && connection_status() == 0 )
    {
        if ($speedLimit != 0)
        {
            list($usec, $sec) = explode(" ", microtime()); 
            $outputTimeStart = ((float)$usec + (float)$sec);
        }
        $readBufferSize = $contentLength - $bytesSent < $bufferSize ? $contentLength - $bytesSent : $bufferSize;
        $buffer = fread($fp, $readBufferSize);        
        echo $buffer;
        ob_flush();
        flush();
        $bytesSent += $readBufferSize;
        if ($speedLimit != 0)
        {
            list($usec, $sec) = explode(" ", microtime()); 
            $outputTimeEnd = ((float)$usec + (float)$sec);
            $useTime = ((float) $outputTimeEnd - (float) $outputTimeStart) * 1000000;
            $sleepTime = round($packetTime - $useTime);
            if ($sleepTime > 0)
            {
                usleep($sleepTime);
            }
        }
    }
    
    return true;
}
 ?>
PHP 相关文章推荐
《APMServ 5.1.2》使用图解
Oct 23 PHP
小偷PHP+Html+缓存
Nov 25 PHP
用PHP实现将GB编码转换为UTF8
Nov 25 PHP
Discuz 6.0+ 批量注册用户名
Sep 13 PHP
使用NetBeans + Xdebug调试PHP程序的方法
Apr 12 PHP
逆序二维数组插入一元素的php代码
Jun 08 PHP
php之XML转数组函数的详解
Jun 07 PHP
php使用curl打开https网站的方法
Jun 17 PHP
php实现登陆模块功能示例
Oct 20 PHP
Laravel框架实现利用中间件进行操作日志记录功能
Jun 06 PHP
浅谈php://filter的妙用
Mar 05 PHP
Laravel 框架路由原理与路由访问实例分析
Apr 14 PHP
preg_match_all使用心得分享
Jan 31 #PHP
基于preg_match_all采集后数据处理的一点心得笔记(编码转换和正则匹配)
Jan 31 #PHP
php curl_init函数用法
Jan 31 #PHP
curl实现站外采集的方法和技巧
Jan 31 #PHP
php使用curl检测网页是否被百度收录的示例分享
Jan 31 #PHP
php使用百度翻译api示例分享
Jan 31 #PHP
php比较两个绝对时间的大小
Jan 31 #PHP
You might like
PHP实现文件安全下载
2006/10/09 PHP
深入探讨PHP中的内存管理问题
2011/08/31 PHP
收集的网上用的ajax之chat.js文件
2007/04/08 Javascript
javascript 动态添加事件代码
2008/11/30 Javascript
Javascript 同时提交多个Web表单的方法
2009/02/19 Javascript
JQuery 常用方法和事件详细介绍
2013/04/18 Javascript
jquery 提示信息显示后自动消失的具体实现
2013/12/18 Javascript
JS输入用户名自动显示邮箱后缀列表的方法
2015/01/27 Javascript
javascript实现日期时间动态显示示例代码
2015/09/08 Javascript
js和jQuery设置Opacity半透明 兼容IE6
2016/05/24 Javascript
angularjs点击图片放大实现上传图片预览
2017/02/24 Javascript
详解Vue.js基于$.ajax获取数据并与组件的data绑定
2017/05/26 Javascript
node.js调用C++函数的方法示例
2018/09/21 Javascript
基于Vue实现图片在指定区域内移动的思路详解
2018/11/11 Javascript
JS 封装父页面子页面交互接口的实例代码
2019/06/25 Javascript
element表格翻页第2页从1开始编号(后端从0开始分页)
2019/12/10 Javascript
JavaScript 装逼指南(js另类写法)
2020/05/10 Javascript
Django中URLconf和include()的协同工作方法
2015/07/20 Python
python数据类型_元组、字典常用操作方法(介绍)
2017/05/30 Python
Python基础学习之基本数据结构详解【数字、字符串、列表、元组、集合、字典】
2019/06/18 Python
详解Python 最短匹配模式
2020/07/29 Python
在 Python 中使用 MQTT的方法
2020/08/18 Python
CSS3 实现弹跳的小球动画
2020/10/26 HTML / CSS
美国美发品牌:Bumble and Bumble
2016/10/08 全球购物
全球最大的游戏市场:G2A
2018/07/05 全球购物
写给老师的表扬信
2014/01/21 职场文书
人事专员的岗位职责
2014/03/01 职场文书
经理秘书求职自荐信范文
2014/03/23 职场文书
个人自荐材料
2014/05/23 职场文书
英语专业求职信
2014/07/08 职场文书
基层党员对照检查材料
2014/09/24 职场文书
婚礼必备主持词范本!
2019/07/23 职场文书
Promise面试题详解之控制并发
2021/05/14 面试题
如何理解Vue简单状态管理之store模式
2021/05/15 Vue.js
openstack中的rpc远程调用的方法
2021/07/09 Python
详解ZABBIX监控ESXI主机的问题
2022/06/21 Servers