利用php实现一个简单类,支持文件断点续传。
只是个demo, http range实现并不完整,但是对于普通的浏览器的下载没有问题。

<?php
//PHPXFileDownload v1.0
class File{
    private $fp;
    private $file;
    private $name;
    private $path;

    private $mime;
    private $size;

    public function __construct($filename,$filepath){
        ob_start();
        $this->name = $filename;
        $this->path = $filepath;
        $this->file = "$filepath/$filename";
        if (!$this->initFile()){
            throw new Exception("File Error!");
        }
    }

    public function __destruct(){            
        if ($this->fp){
            flock($this->fp, LOCK_UN);
            fclose($this->fp);
        }
    }

    private function initFile(){
        if (!file_exists($this->file))
            return false;

        $f = new finfo(FILEINFO_MIME_TYPE); 
        $this->mime = $f->file($this->file);
        if ($this->mime === FALSE)
            return false;
        $this->size = filesize($this->file);
        if ($this->size === FALSE)
            return false;
        $this->fp = fopen($this->file,"rb");
        if ($this->fp === FALSE)
            return false;
        $ret = flock($this->fp, LOCK_SH | LOCK_NB);
        if ($ret === FALSE){
            fclose($this->fp);
            $this->fp = FALSE;
            return false;
        }
        return true;
    }

    public function getSize(){
        return $this->size;
    }
    public function getMime(){
        return $this->mime;
    }

    public function download(){
        if (ob_get_contents() != ""){
            throw new Exception("Have output before the file transfer");
        }
        ob_end_clean();

        $start = 0;
        $end = $this->size - 1;
        if(isset($_SERVER['HTTP_RANGE'])) { //客户端要求断点续传
            $range = $this->getRange($_SERVER['HTTP_RANGE']);
            if ($range === false || $this->checkRange($range[0],$range[1])){
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                return false;
            }
            $start = (int)($range[0]);
            $end = (int)($range[1]);
            header('HTTP/1.1 206 Partial Content');
            header('Content-Length:'.($end - $start + 1));
            header('Content-Range: bytes '.$start.'-'.$end.'/'.$this->size);
            $this->sendCommonHead();
        }else{ //普通传输
            header('Content-Length:'.$this->size);
            $this->sendCommonHead();
        }
        fseek($this->fp,$start);
        echo fread($this->fp,$end - $start + 1);
    }

    private function checkRange($start,$end){
        if ($start < $end)
            return false;
        if ($end>=$this->size)
            return false;

        return true;
    }

    private function getRange($http_range){
        $range = [];
        $http_range = substr($http_range,strpos($http_range,"=")+1);
        $http_range = explode('-',$http_range);
       
        if (count($http_range)!=2)
            return false;
        if ($http_range[0] == "")
            $range[0] = 0;
        else
            $range[0] = (int)($http_range[0]);

        if ($http_range[1] == "")
            $range[1] = $this->size - 1;
        else
            $range[1] = (int)($http_range[1]);

        if ($range[0] > $range[1])
            return false;
        return $range;
    }

    private function sendCommonHead(){
        header('Content-Disposition: attachment; filename="' . $this->name .'"' );
        header("Content-Type: application/octet-stream");
        header('Accept-Ranges: bytes');
    }
}


##测试代码
$file = new File("test.mp3","./");
$file->download();