PHP Remote file streaming with Resume Support PHP Remote file streaming with Resume Support php php

PHP Remote file streaming with Resume Support


You can try implementing your own download script using Accept-Ranges and Content-Range here is a prof of concept :

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

Using Internet Download Manager

Start

Start

Paused

Paused

Paused State

Paused State

Resume

Resume

Finished

enter image description here

Class Used

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


If you're using PHP to serve the file, you have to implement all resuming logic yourself.

You'll have to send Accept-Ranges and respond appropriately to Ranges.

That's a chunk of work. It might be easier to use mod_proxy.


What's the purpose of this? hiding urls only or just allowing members to download?

The way you described it, it's a bit tricky ...

  1. The remote server your script will download from should support resuming downloads.
  2. Your php script should check for 'Accept-Range' header & pass it through to the remote server (using sockets is your best option I guess) so your script is actually acting as a proxy.