分享一个超好用的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 相关文章推荐
第八节 访问方式 [8]
Oct 09 PHP
一个查看session内容的函数
Oct 09 PHP
PHP+javascript模拟Matrix画面
Oct 09 PHP
PHP给文字内容中的关键字进行套红处理
Apr 12 PHP
PHP实现的进度条效果详解
May 03 PHP
iOS10推送通知开发教程
Sep 19 PHP
Ubuntu VPS中wordpress网站打开时提示”建立数据库连接错误”的解决办法
Nov 03 PHP
PHP实现类似于C语言的文件读取及解析功能
Sep 01 PHP
thinkPHP5.1框架使用SemanticUI实现分页功能示例
Aug 03 PHP
php和html的区别点详细总结
Sep 24 PHP
PHP的new static和new self的区别与使用
Nov 27 PHP
PHP如何使用JWT做Api接口身份认证的实现
Feb 03 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
快速配置PHPMyAdmin方法
2008/06/05 PHP
用Laravel Sms实现laravel短信验证码的发送的实现
2018/11/29 PHP
经常用到的JavasScript事件的翻译
2007/04/09 Javascript
javascript 异常处理使用总结
2009/06/21 Javascript
Javascript之旅 对象的原型链之由来
2010/08/25 Javascript
早该知道的7个JavaScript技巧
2013/03/27 Javascript
基于replaceChild制作简单的吞噬特效
2015/09/21 Javascript
jquery背景跟随鼠标滑动导航
2015/11/20 Javascript
一道优雅面试题分析js中fn()和return fn()的区别
2016/07/05 Javascript
解决拦截器对ajax请求的拦截实例详解
2016/12/21 Javascript
js实现一个猜数字游戏
2017/03/31 Javascript
学习使用Bootstrap输入框、导航、分页等常用组件
2017/05/11 Javascript
基于Bootstrap实现城市三级联动
2017/11/23 Javascript
如何将HTML字符转换为DOM节点并动态添加到文档中详解
2018/08/19 Javascript
vue如何安装使用Quill富文本编辑器
2018/09/21 Javascript
使用canvas实现一个vue弹幕组件功能
2018/11/30 Javascript
详解vue更改头像功能实现
2019/04/28 Javascript
解决vue+router路由跳转不起作用的一项原因
2020/07/19 Javascript
如何在 Vue 表单中处理图片
2021/01/26 Vue.js
[05:02]2014DOTA2 TI中国区预选赛精彩TOPPLAY第三弹
2014/06/25 DOTA
Python 中urls.py:URL dispatcher(路由配置文件)详解
2017/03/24 Python
基于Python数据可视化利器Matplotlib,绘图入门篇,Pyplot详解
2017/10/13 Python
pandas 将list切分后存入DataFrame中的实例
2018/07/03 Python
Tensorflow模型实现预测或识别单张图片
2019/07/19 Python
Python 分布式缓存之Reids数据类型操作详解
2020/06/24 Python
安全生产责任书
2014/03/12 职场文书
如何写自我鉴定
2014/03/19 职场文书
创业融资计划书
2014/04/25 职场文书
安全承诺书格式
2014/05/21 职场文书
安全资料员岗位职责范本
2014/06/28 职场文书
优秀班主任先进事迹材料
2014/12/16 职场文书
2015年组织委员工作总结
2015/04/23 职场文书
领导干部失职检讨书
2015/05/05 职场文书
golang fmt格式“占位符”的实例用法详解
2021/07/04 Golang
mysql聚集索引、辅助索引、覆盖索引、联合索引的使用
2022/02/12 MySQL
Python可视化学习之seaborn绘制矩阵图详解
2022/02/24 Python