Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PHP - resumable download
<?php
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 = '/home/file.zip';
set_time_limit(0);
$download = new ResumeDownload($file, 50000); //delay about in microsecs
$download->process();
@shaunjstokes

This comment has been minimized.

Copy link

@shaunjstokes shaunjstokes commented Apr 7, 2016

Great script, works perfectly and has saved me lots of time. Thanks!

@MyPC8MyBrain

This comment has been minimized.

Copy link

@MyPC8MyBrain MyPC8MyBrain commented Jun 14, 2017

Thank you for sharing Nico,
finally a working resume script,
one question though... why is it so slow?
(normal download is 2-3mb sec; while this script remains connected and resumes it is painfully slow as 45kb sec compared to normal)

Chris

@hamedghari

This comment has been minimized.

Copy link

@hamedghari hamedghari commented Jun 28, 2017

thank you very much, it is so great code. I use it ,works very well ^__^

@sameedalam12

This comment has been minimized.

Copy link

@sameedalam12 sameedalam12 commented Aug 12, 2017

@kosinix thanks its working fine on download manager with resume and good speed but on browser download its speed very low 45kbps how to solve it

@kosinix

This comment has been minimized.

Copy link
Owner Author

@kosinix kosinix commented Nov 9, 2017

For everyone asking why download is slow, turn off the delay option:

$download = new ResumeDownload($file, 50000); //delay about in microsecs

to

$download = new ResumeDownload($file); // no delay

That delay was for demo purposes only when dealing with download managers.

@GaalexxC

This comment has been minimized.

Copy link

@GaalexxC GaalexxC commented Nov 28, 2017

Throws 500 error with PHP 7.1, looking into the issue.

Opps its your alteration:
$download = new ResumeDownload($file,);
Remove the comma after $file
$download = new ResumeDownload($file);

Thanks for the Gist

@totop275

This comment has been minimized.

Copy link

@totop275 totop275 commented Jan 4, 2018

Thanks for this, it works great. i modified some code and use it on my project. thanks

@sameedalam12

This comment has been minimized.

Copy link

@sameedalam12 sameedalam12 commented Apr 30, 2020

i face one problem on it. when multi download at a time like 300+ download . then your server site and file processing slow like its take 30 second to 3 minutes.
i have good sever
16gb ram
8 core processor
ssd drive

I think this script every file open through ram then its problem occur . because I see our ram use as a cache full
please give a solution for it

@alecos71

This comment has been minimized.

Copy link

@alecos71 alecos71 commented May 20, 2020

@sameedalam12: the script reads byte per byte but in fastest way. If you have a lot of files and your files are very big, it's normal a delay in response to download action. This is the fastest script that I seen. The only way to have no delay is serving normal links in your files to download instead a script that have to read each single byte per byte, the files are not supplied directly but virtualized, the script first read byte per byte and then send the output to the browser.

If you use the script like suggested by @kosinix the problem is very mitigated...

wrong:

$download = new ResumeDownload($file, 50000); //delay about in microsecs

correct:

$download = new ResumeDownload($file); // no delay

Have a nice day! Greetings.

@xcoders-hub

This comment has been minimized.

Copy link

@xcoders-hub xcoders-hub commented Nov 19, 2020

Thanks, your code works perfectly if file is in our local server. But if i try to download from url it shows file not found. Anyway to solve this issue.

@alecos71

This comment has been minimized.

Copy link

@alecos71 alecos71 commented Nov 19, 2020

@xcoders-hub, you have to provide full path because local server and remote server have different structure ($_SERVER["DOCUMENT_ROOT"]). For me works great both on local server and remote server.

@xcoders-hub

This comment has been minimized.

Copy link

@xcoders-hub xcoders-hub commented Nov 19, 2020

It works on same server but if the url is from another server it says file not found.

@alecos71

This comment has been minimized.

Copy link

@alecos71 alecos71 commented Nov 19, 2020

@xcoders-hub, the script in on server 1, the file is on server 2, you want provide the file on server 2 using the script that is on the server 1, the script is server side, cannot read remote file size, can only read local file size...

Therefore is dangerous to perform a download using remote connection because someone in the url could put something of evil, the script must determine the file size and cannot doing this on remote files. The range for resume download is determined by file size without cannot work as expected.

@xcoders-hub

This comment has been minimized.

Copy link

@xcoders-hub xcoders-hub commented Nov 19, 2020

Thanks mate , I'm looking solution for Google drive resumable download. For large files drive download cancelled most of the times on slow internet connections.

@mreduar

This comment has been minimized.

Copy link

@mreduar mreduar commented Mar 10, 2021

@xcoders-hub Did you find a solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment