美文网首页
php下载大文件,支持断点续传案例

php下载大文件,支持断点续传案例

作者: 胡乱唱歌ing | 来源:发表于2018-12-10 11:36 被阅读0次

    思路
    1.设置header头信息
    2.使用php的输出缓冲ob_flush,flush
    2.下载速度*fread(size) ,sleep(1)达到限制下载速度效果
    3.通过每次传输的大小,剩余的大小来偏移读取文件的指针位置达到断点续传
    4.只能限制到最大的下载速度,具体的速度根据实际的网络环境会有偏差

    /**
     *  @param [string] $[file] [文件路径]
     *  @param [int] $[rate] [下载速度]
     *  @param boole $[forceDownload] [文件名是否中文处理]
     */
    
    function downFile($file,$rate=100,$forceDownload=true)
    {
        
        if(!file_exists($file))
        {
            header("HTTP/1.1 404 Not Found");
            return false;
        }
        if(!is_readable($file)) {
            header("HTTP/1.1 404 Not Found");
            return false;
        }
    
        #读取文件的信息
        $fileStat = stat($file);
        $lastModified = $fileStat['mtime'];
    
        #拼成etag,防止文件发生修改
        $md5 = md5($fileStat['mtime'] . '=' . $fileStat['ino'] . '=' . $fileStat['size']);
        $etag = '"' . $md5 . '-' . crc32($md5) . '"';
    
        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;
        }
    
    
        $fileSize = $fileStat['size'];
        $contentLength = $fileSize;//文件大小
        $isPartial = false;//是否断点续传
        $fancyName = basename($file);
    
        //计算断点续传的开始位置
        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;
            }
        }
       //断点续传 记录下次下载的位置
        if ($isPartial) {
            header('HTTP/1.1 206 Partial Content');
            header(sprintf('Content-Range:bytes %s-%s/%s', $startPos, $endPos, $fileSize));
    
        } else {
            header("HTTP/1.1 200 OK");
            $startPos = 0;
            $endPos = $contentLength - 1;
        }
        //设置header头
        header('Pragma: cache');
        header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT');
        header("ETag: $etag");
        header('Cache-Control: public, must-revalidate, max-age=0');
        header('Accept-Ranges: bytes');
        header('Content-Length: ' . $contentLength);
        header("Content-Type: application/force-download");
        header("Content-Type: application/octet-stream");
        header("Content-Type: application/download");
    
        //对不同浏览器进行中文设置,避免下载导致文件名乱码
        if ($forceDownload) {
                //处理中文文件名
            $ua = $_SERVER["HTTP_USER_AGENT"];
            if (preg_match("/MSIE/", $ua)) {
                header('Content-Disposition: attachment; filename="' . rawurlencode($fancyName) . '"');
            } else if (preg_match("/Firefox/", $ua)) {
                header('Content-Disposition: attachment; filename*="utf8\'\'' . rawurlencode($fancyName));
            } else {
                header('Content-Disposition: attachment; filename="' . $fancyName . '"');
            }
        }else
        {
            header("Content-Disposition: attachment; filename=" . $fancyName);
        }
    
        $bufferSize = 1024;//设置最小读取字节数 1kb
        //判断是否有设置下载速度
        if($rate > 0) 
        {
            $bufferSize = $rate * $bufferSize; //100*1024 下载速度最大为100KB/s
        }
        
        $bytesSent = 0;
        $outputTimeStart = 0.00;
        $fp = fopen($file, "rb");
        fseek($fp, $startPos);
    
        while ($bytesSent < $contentLength && !feof($fp) && connection_status() == 0) {
            $readBufferSize = $contentLength - $bytesSent < $bufferSize ? $contentLength - $bytesSent : $bufferSize;
            $buffer = fread($fp, $readBufferSize);
            echo $buffer;
            //输出缓冲
            if(ob_get_level()>0)
            {
                ob_flush();
            }
            flush();
            $bytesSent += $readBufferSize;
            sleep(1); //睡眠一秒 这里也就是限制下载速度的关键 一秒只读$readBufferSize字节
        }
        if($fp) fclose($fp); 
    }
    $file = './files/centos.zip';
    $rate = 1000; //下载速度 单位 kb/s
    downFile($file,$rate,true);
    
    image.png

    相关文章

      网友评论

          本文标题:php下载大文件,支持断点续传案例

          本文链接:https://www.haomeiwen.com/subject/rolrhqtx.html