Last active
June 21, 2018 15:15
-
-
Save noelboss/7815ff2fea96b41544672f5e1a15f1f5 to your computer and use it in GitHub Desktop.
Range Download – Usage: wire('modules')->get('ProcessPageView')->sendFile($page, $filename);
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* © noelboss.com | |
* | |
* This source file is subject to the license file that is bundled | |
* with this source code in the file LICENSE. | |
* | |
* File created/changed: 2018-06-21T16:24:02+02:00 | |
*/ | |
namespace ProcessWire; | |
/* | |
* This _init.php file is called automatically by ProcessWire before every page render. | |
*/ | |
class RangeDownloadBoss extends WireData implements Module | |
{ | |
/** | |
* getModuleInfo is a module required by all modules to tell ProcessWire about them. | |
* | |
* @return array | |
*/ | |
public static function getModuleInfo() | |
{ | |
return [ | |
// The module'ss title, typically a little more descriptive than the class name | |
'title' => 'Range Download Boss', | |
// version number | |
'version' => '0.0.1', | |
// summary is brief description of what this module is | |
'summary' => 'Adds range downloads for videos etc.', | |
// Optional URL to more information about the module | |
'href' => '', | |
// singular=true: indicates that only one instance of the module is allowed. | |
// This is usually what you want for modules that attach hooks. | |
'singular' => true, | |
// autoload=true: indicates the module should be started with ProcessWire. | |
// This is necessary for any modules that attach runtime hooks, otherwise those | |
// hooks won't get attached unless some other code calls the module on it's own. | |
// Note that autoload modules are almost always also 'singular' (seen above). | |
'autoload' => true, | |
// Optional font-awesome icon name, minus the 'fa-' part | |
'icon' => 'download', | |
]; | |
} | |
public function init() | |
{ | |
wire()->addHookBefore('ProcessPageView::sendFile', $this, 'sendFileHook'); | |
} | |
public function sendFileHook(HookEvent $event) | |
{ | |
$page = $event->arguments[0]; | |
$file; | |
if ($page->hasField('files')) { | |
$file = $page->files->get($event->arguments[1]); | |
} elseif ($page->hasField('image')) { | |
$file = $page->image->get($event->arguments[1]); | |
} | |
if (!$file instanceof Pagefile && !$file instanceof Pageimage) { | |
return; | |
} | |
$this->downloadRange($file); | |
} | |
/** | |
* Send a File as Range. | |
* | |
* @param HookEvent $event | |
* @param mixed $file | |
*/ | |
private function downloadRange($file) | |
{ | |
if (!is_file($file->filename)) { | |
throw new Wire404Exception('Page not found'); | |
} | |
$fp = @fopen($file->filename, 'rb'); | |
$size = filesize($file->filename); // File size | |
$length = $size; // Content length | |
$start = 0; // Start byte | |
$end = $size - 1; // End byte | |
if (isset(wire('config')->fileContentTypes[$file->ext])) { | |
$ctype = wire('config')->fileContentTypes[$file->ext]; | |
} else { | |
$ctype = mime_content_type($file->filename); | |
} | |
header('Content-type: '.$ctype); | |
header("Accept-Ranges: 0-$length"); | |
if (isset($_SERVER['HTTP_RANGE'])) { | |
$c_start = $start; | |
$c_end = $end; | |
list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); | |
if (strpos($range, ',') !== false) { | |
header('HTTP/1.1 416 Requested Range Not Satisfiable'); | |
header("Content-Range: bytes $start-$end/$size"); | |
exit; | |
} | |
if ($range == '-') { | |
$c_start = $size - substr($range, 1); | |
} else { | |
$range = explode('-', $range); | |
$c_start = $range[0]; | |
$c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; | |
} | |
$c_end = ($c_end > $end) ? $end : $c_end; | |
if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { | |
header('HTTP/1.1 416 Requested Range Not Satisfiable'); | |
header("Content-Range: bytes $start-$end/$size"); | |
exit; | |
} | |
$start = $c_start; | |
$end = $c_end; | |
$length = $end - $start + 1; | |
fseek($fp, $start); | |
header('HTTP/1.1 206 Partial Content'); | |
} | |
header("Content-Range: bytes $start-$end/$size"); | |
header('Content-Length: '.$length); | |
$buffer = 1024 * 8; | |
while (!feof($fp) && ($p = ftell($fp)) <= $end) { | |
if ($p + $buffer > $end) { | |
$buffer = $end - $p + 1; | |
} | |
set_time_limit(0); | |
echo fread($fp, $buffer); | |
flush(); | |
} | |
fclose($fp); | |
exit(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment