利用PHP实现在线下载大文件

在实现开发过程中, 我们会遇到下载问题。 在我们考虑的前提是安全,所以大家都设置了用php来实现下载, 就是header的应用, 而header的普通应用在应付小文件时,比如20MB,40MB的还行。 但如果项目中增加了许可下载1GB 4GB呢?比如游戏文件, iso文件, 那就普通的header就有点力不从心。 根据测试, 文件下载过程还是会实现, 不过下载下来的文件是0字节,即空文件。
这就是大家一直讲的echo 大文件时速度慢,导致线程被中断等等问题怕造成的, 而我们这些应该要记住php还有一个缓存区,即ob_flush 

  也许用如下的函数可以解决大文件下载。
[code language=php]<?php
$file_path = './download/download_cn.rar';

//使用方法
downFile($file_path);

// 服务器文件路径,下载文件名字(默认为服务器文件名),是否许可用户下载方式(默认可选),速度限制(默认自动),文件类型(默认所有)
function downFile($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). '"'); 
    } 
  
    header("Content-Transfer-Encoding: binary"); 
  
    $bufferSize = 2048; 
  
    if ($speedLimit != 0) 
    { 
        $packetTime = floor($bufferSize * 1000000 / $speedLimit); 
    } 
  
    $bytesSent = 0; 
    $fp = fopen($fileName, "rb"); 
    fseek($fp, $startPos); 
    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; 

?>[/code]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值