This code example should provide enough in order to see how the process works. It attempts to handle errors, and range requests in a sane way.
$this->mdb = connect_mongodb();
$this->gdb = $this->mdb->getGridFS();
$fh = $this->gdb->findOne(array('filename' => $filename));
if ($fh == null ) {
header("HTTP/1.1 404 Not Found");
return false;
}
$stream = $fh->getResource();
$length = $fh->file['length'];
// Check for range request
if (isset($_SERVER['HTTP_RANGE'])) {
$range = explode('=',$_SERVER['HTTP_RANGE'],2);
if (strtolower(trim($range[0])) != 'bytes') {
header('HTTP/1.1 416 Requested Range Not Satisfiable');
return false;
}
if (substr_count($range[1],',') >= 1) {
//TODO handle multi ranges
}
if ($range[1]{0} == '-') {
//range begins in reverse
$req_bytes = substr($range[1],1);
(int)$start = (int)$length - 1 - (int)$req_bytes;
(int)$end = (int)$length - 1;
}
if (is_numeric(substr($range[1],0,1)) === true) {
$dash_position = strpos($range[1], '-');
$str_length = strlen($range[1]);
if (($str_length - 1) == $dash_position) {
//Dash position on the end, so we want the remaining amount
$start = (int)$range[1];
$end = (int)$length - 1;
} else {
//There's something after the dash
$r_parts = explode('-',$range[1],2);
$start = (int)$r_parts[0];
$end = (int)$r_parts[1];
}
}
$c_length = $end - $start + 1;
$a = $end - $start;
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes {$start}-{$end}/{$length}");
header("Content-Length: {$c_length}");
fseek($stream, $start);
} else {
header("Content-Length: " . $fh->file['length']);
$end = $length - 1;
}
header("Content-type: video/{$matches[3]}");
header("Content-Disposition: inline");
header("Accept-Ranges: bytes");
header("X-Accel-Buffering: no");
$out = 0;
$bytes = 8192;
ob_implicit_flush(true);
ob_end_flush();
set_time_limit(0);
do {
if ($end < $bytes) {
$bytes = $end + 1;
}
echo fread($stream, $bytes);
flush();
$out = $out + $bytes;
$end = $end - $bytes;
if ($end <= 0) {
break;
}
if (connection_status() != CONNECTION_NORMAL) {
break;
}
} while (true);
fclose($stream);
While this example doesn't handle every possible combination of range requests, in my testing it handles every case I've come across.
No comments:
Post a Comment