-
-
Save z2z/00148839e292d5cb29d1 to your computer and use it in GitHub Desktop.
PHP Any File Stream With HTTP_RANGE
This file contains 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 | |
/** | |
* Description of FileStream | |
* | |
* @author Rana | |
* @link http://codesamplez.com/programming/php-html5-video-streaming-tutorial | |
* @co-author Lukas Stribrny | |
* Added support for fileinfo,mod_ratelimit | |
* | |
*/ | |
class FileStream { | |
private $path = ""; | |
private $stream = ""; | |
private $buffer = 102400; | |
private $start = -1; | |
private $end = -1; | |
private $size = 0; | |
private $enable_range = 0; | |
public $attachment = false; | |
public $basename; | |
public $megabytes_speed = 0.5; | |
private $bytes_speed = 1024; | |
function __construct(){ | |
} | |
public function SetFile($filePath){ | |
$this->path = $filePath; | |
//- turn off compression on the server | |
@apache_setenv('no-gzip', 1); | |
@ini_set('zlib.output_compression', 'Off'); | |
} | |
public function SetFileName($FileName){ | |
$this->basename = $FileName; | |
} | |
public function SetSpeed($speed){ | |
$this->megabytes_speed = $speed; | |
} | |
public function speed(){ | |
if(in_array('mod_ratelimit',$this->apache_modules)){ | |
apache_setenv("rate-limit",($this->megabytes_speed * $this->bytes_speed)); | |
}else{ | |
error_log("ERROR:mod_ratelimit not available and can not set the download speed!", 0); | |
} | |
} | |
public function Resumable(){ | |
$this->enable_range = true; | |
} | |
/** | |
* Open stream | |
*/ | |
private function open(){ | |
$error_message = NULL; | |
if($this->path==true){ | |
if(file_exists($this->path)){ | |
if(is_file($this->path)){ | |
if($this->enable_range==true){ | |
if (!($this->stream = fopen($this->path, 'rb'))){ | |
$error_message = 'ERROR:Could not open stream for reading'; | |
} | |
} | |
}else{ | |
$error_message = "ERROR:The object is not a file!"; | |
} | |
}else{ | |
$error_message = "ERROR:The file not exist!"; | |
} | |
}else{ | |
$error_message = "ERROR:The file path is missing!"; | |
} | |
if($error_message==TRUE){ | |
error_log($error_message, 0); | |
die($error_message); | |
} | |
} | |
/** | |
* Set proper header to serve the content | |
*/ | |
private function mime_content_type(){ | |
if(function_exists('finfo_open')){ | |
$info = finfo_open(FILEINFO_MIME_TYPE); | |
$mime_type = finfo_file($info, $this->path); | |
finfo_close($info); | |
}else{ | |
error_log("ERROR:fileinfo and FILEINFO_MIME_TYPE not available!", 0); | |
$mime_type = 'application/octet-stream'; | |
} | |
$this->path_parts = pathinfo($this->path); | |
$this->path_parts['mime_type'] = $mime_type; | |
$this->path_parts['file_size'] = filesize($this->path); | |
} | |
private function AttachmentHeader(){ | |
if($this->basename){ | |
$basename = $this->basename.'.'.$this->path_parts['extension']; | |
}else{ | |
$basename = $this->path_parts['basename']; | |
} | |
// allow a file to be streamed instead of sent as an attachment | |
$disposition = $this->attachment ? 'attachment' : 'inline'; | |
// set appropriate headers for attachment or streamed file | |
header('Content-Disposition: '.$disposition.'; filename="'.$basename.'"'); | |
if($this->attachment==false){ | |
header('Content-Transfer-Encoding: binary'); | |
} | |
} | |
private function StandartHeader(){ | |
header('Content-Description: File Transfer'); | |
header('Content-Length: '.$this->path_parts['file_size'].''); | |
// set the mime type based on extension. | |
header('Content-Type: '.$this->path_parts['mime_type'].''); | |
$this->AttachmentHeader(); | |
} | |
private function RangeHeader(){ | |
ob_get_clean(); | |
header("Content-Type: ".$this->path_parts['mime_type']); | |
header("Cache-Control: max-age=2592000, public"); | |
header("Expires: ".gmdate('D, d M Y H:i:s', time()+2592000) . ' GMT'); | |
header("Last-Modified: ".gmdate('D, d M Y H:i:s', @filemtime($this->path)) . ' GMT' ); | |
$this->AttachmentHeader(); | |
$this->start = 0; | |
$this->size = $this->path_parts['file_size']; | |
$this->end = $this->size - 1; | |
header("Accept-Ranges: 0-".$this->end); | |
if (isset($_SERVER['HTTP_RANGE'])){ | |
$c_start = $this->start; | |
$c_end = $this->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 $this->start-$this->end/$this->size"); | |
exit; | |
} | |
if ($range == '-'){ | |
$c_start = $this->size - substr($range, 1); | |
}else{ | |
$range = explode('-', $range); | |
$c_start = $range[0]; | |
$c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end; | |
} | |
$c_end = ($c_end > $this->end) ? $this->end : $c_end; | |
if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size){ | |
header('HTTP/1.1 416 Requested Range Not Satisfiable'); | |
header("Content-Range: bytes $this->start-$this->end/$this->size"); | |
exit; | |
} | |
$this->start = $c_start; | |
$this->end = $c_end; | |
$length = $this->end - $this->start + 1; | |
fseek($this->stream, $this->start); | |
header('HTTP/1.1 206 Partial Content'); | |
header("Content-Length: ".$length); | |
header("Content-Range: bytes $this->start-$this->end/".$this->size); | |
}else{ | |
header("Content-Length: ".$this->size); | |
} | |
} | |
/** | |
* close curretly opened stream | |
*/ | |
private function end(){ | |
fclose($this->stream); | |
exit; | |
} | |
/** | |
* perform the streaming of calculated range | |
*/ | |
private function read(){ | |
$i = $this->start; | |
set_time_limit(0); | |
while(!feof($this->stream) && $i <= $this->end) { | |
$bytesToRead = $this->buffer; | |
if(($i+$bytesToRead) > $this->end) { | |
$bytesToRead = $this->end - $i + 1; | |
} | |
$data = fread($this->stream, $bytesToRead); | |
echo $data; | |
flush(); | |
$i += $bytesToRead; | |
} | |
} | |
public function http_range(){ | |
$this->RangeHeader(); | |
$this->read(); | |
$this->end(); | |
} | |
public function http_standart(){ | |
$this->StandartHeader(); | |
if(in_array('mod_xsendfile',apache_get_modules())){ | |
header('X-Sendfile: '.$this->path); | |
}else{ | |
error_log("ERROR:mod_xsendfile not available!", 0); | |
if (@ob_get_level()){ | |
@ob_end_clean(); | |
@ob_end_flush(); | |
} | |
readfile($this->path); | |
} | |
} | |
/** | |
* Start streaming video content | |
*/ | |
public function stream(){ | |
$this->speed(); | |
$this->open(); | |
$this->mime_content_type(); | |
if($this->enable_range==true){ | |
$this->http_range(); | |
}else{ | |
$this->http_standart(); | |
} | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment