【问题标题】:PHP Remote file streaming with Resume Support带简历支持的 PHP 远程文件流式传输
【发布时间】:2010-12-26 01:02:13
【问题描述】:

首先,我知道以前有人问过类似的问题。

这个主题几乎解释了这个问题,但仍然,

文件托管在另一台服务器上,用户将通过我的脚本下载文件,流式传输给他...

但问题是用户一旦暂停就无法恢复...有什么解决方案吗?

【问题讨论】:

    标签: php file download resume


    【解决方案1】:

    您可以尝试使用Accept-RangesContent-Range 实现自己的下载脚本,这是一个概念教授:

    set_time_limit(0);
    $download = new ResumeDownload("word.dir.txt", 50000); //delay about in microsecs 
    $download->process();
    

    使用 Internet 下载管理器

    开始

    暂停

    暂停状态

    简历

    完成

    使用的类

    class ResumeDownload {
        private $file;
        private $name;
        private $boundary;
        private $delay = 0;
        private $size = 0;
    
        function __construct($file, $delay = 0) {
            if (! is_file($file)) {
                header("HTTP/1.1 400 Invalid Request");
                die("<h3>File Not Found</h3>");
            }
    
            $this->size = filesize($file);
            $this->file = fopen($file, "r");
            $this->boundary = md5($file);
            $this->delay = $delay;
            $this->name = basename($file);
        }
    
        public function process() {
            $ranges = NULL;
            $t = 0;
            if ($_SERVER['REQUEST_METHOD'] == 'GET' && isset($_SERVER['HTTP_RANGE']) && $range = stristr(trim($_SERVER['HTTP_RANGE']), 'bytes=')) {
                $range = substr($range, 6);
                $ranges = explode(',', $range);
                $t = count($ranges);
            }
    
            header("Accept-Ranges: bytes");
            header("Content-Type: application/octet-stream");
            header("Content-Transfer-Encoding: binary");
            header(sprintf('Content-Disposition: attachment; filename="%s"', $this->name));
    
            if ($t > 0) {
                header("HTTP/1.1 206 Partial content");
                $t === 1 ? $this->pushSingle($range) : $this->pushMulti($ranges);
            } else {
                header("Content-Length: " . $this->size);
                $this->readFile();
            }
    
            flush();
        }
    
        private function pushSingle($range) {
            $start = $end = 0;
            $this->getRange($range, $start, $end);
            header("Content-Length: " . ($end - $start + 1));
            header(sprintf("Content-Range: bytes %d-%d/%d", $start, $end, $this->size));
            fseek($this->file, $start);
            $this->readBuffer($end - $start + 1);
            $this->readFile();
        }
    
        private function pushMulti($ranges) {
            $length = $start = $end = 0;
            $output = "";
    
            $tl = "Content-type: application/octet-stream\r\n";
            $formatRange = "Content-range: bytes %d-%d/%d\r\n\r\n";
    
            foreach ( $ranges as $range ) {
                $this->getRange($range, $start, $end);
                $length += strlen("\r\n--$this->boundary\r\n");
                $length += strlen($tl);
                $length += strlen(sprintf($formatRange, $start, $end, $this->size));
                $length += $end - $start + 1;
            }
            $length += strlen("\r\n--$this->boundary--\r\n");
            header("Content-Length: $length");
            header("Content-Type: multipart/x-byteranges; boundary=$this->boundary");
            foreach ( $ranges as $range ) {
                $this->getRange($range, $start, $end);
                echo "\r\n--$this->boundary\r\n";
                echo $tl;
                echo sprintf($formatRange, $start, $end, $this->size);
                fseek($this->file, $start);
                $this->readBuffer($end - $start + 1);
            }
            echo "\r\n--$this->boundary--\r\n";
        }
    
        private function getRange($range, &$start, &$end) {
            list($start, $end) = explode('-', $range);
    
            $fileSize = $this->size;
            if ($start == '') {
                $tmp = $end;
                $end = $fileSize - 1;
                $start = $fileSize - $tmp;
                if ($start < 0)
                    $start = 0;
            } else {
                if ($end == '' || $end > $fileSize - 1)
                    $end = $fileSize - 1;
            }
    
            if ($start > $end) {
                header("Status: 416 Requested range not satisfiable");
                header("Content-Range: */" . $fileSize);
                exit();
            }
    
            return array(
                    $start,
                    $end
            );
        }
    
        private function readFile() {
            while ( ! feof($this->file) ) {
                echo fgets($this->file);
                flush();
                usleep($this->delay);
            }
        }
    
        private function readBuffer($bytes, $size = 1024) {
            $bytesLeft = $bytes;
            while ( $bytesLeft > 0 && ! feof($this->file) ) {
                $bytesLeft > $size ? $bytesRead = $size : $bytesRead = $bytesLeft;
                $bytesLeft -= $bytesRead;
                echo fread($this->file, $bytesRead);
                flush();
                usleep($this->delay);
            }
        }
    }
    

    File Used

    【讨论】:

    • 真的很棒 1+
    【解决方案2】:

    如果您使用 PHP 来提供文件,则必须自己实现所有恢复逻辑。

    您必须将Accept-Rangesrespond appropriately 发送到Ranges

    这是一大堆工作。使用mod_proxy 可能更容易。

    【讨论】:

      【解决方案3】:

      这样做的目的是什么?只隐藏网址还是只允许会员下载?

      你描述的方式,有点棘手……

      1. 您的脚本将从其下载的远程服务器应支持恢复下载。
      2. 您的 php 脚本应检查“Accept-Range”标头并将其传递给远程服务器(我猜使用套接字是您的最佳选择),因此您的脚本实际上充当了代理。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-06
        • 2016-01-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多