http range
来源:互联网 发布:linux 没有service 编辑:程序博客网 时间:2024/05/22 01:39
<?php// TODO: 权限判断// 文件$file = __DIR__ . '/numbers.txt';$content_type = 'text/plain';// 检查是否可读,并得到文件的大小if (($file_length = filesize($file)) === false){ error_log("Problem reading filesize of $file.");}// 解析首部来确定发送响应所需的信息if (isset($_SERVER['HTTP_RANGE'])){ // 定界符不区分大小写 if ( ! preg_match('/bytes=\d*-\d*(,\d*-\d*)*$/i', $_SERVER['HTTP_RANGE'])) { error_log("Client requestd invalid Range."); send_error($file_length); exit; } /* * 规范: “客户在一个请求多个字节范围(byte-ranges)时,服务器应当按它们在请求中出现的顺序返回这些范围。” */ $ranges = explode(',', substr($_SERVER['HTTP_RANGE'], 6)); // 字节 = 后面的所有内容 $offsets = array(); // 抽取和验证每个部分 // 只保存通过验证的部分 foreach($ranges as $range) { $offset = parse_offset($range, $file_length); if ($offset !== false) { $offsets[] = $offset; } } /* * 取决于所请求的合法范围的个数,必须采用不同的格式返回响应 */ switch (count($offsets)) { case 0: // 非合法范围 error_log("Client requested no valid ranges."); send_error($file_length); exit; break; case 1: // 一个合法阀内,发送标准应答 http_response_code(206); // 部分内容 list($start, $end) = $offsets[0]; header("Content-Range: bytes $start-$end/$file_length"); header("Content-Type: $content_type"); // 设置变量,从而可以在这里以及下一个情况中重用代码(为什么我觉得这是一个很蠢的注意 ==!) // 注意: 0-0为1字节,因为范围包含其两个端点 $content_length = $end - $start + 1; $boundaries = array(0 => '', 1 => ''); break; default: // 多个合法范围,发送多部分应答 http_response_code(206); // 部分应答 $boundary = str_rand(32); // 分隔各个部分的字符串 /* * 需要计算整个响应的内容长度(Content-Length),不过将整个响应加载到一个字符串中占用大量的内存就,所以使用偏移量计算值。另外利用这个机会计算边界。 */ $boundaries = array(); $content_length = 0; foreach ($offsets as $offset) { list($start, $end) = $offset; // 用于分解各个部分 $boundary_header = "\r\n" . "--$boundary\r\n" . "Content-Type: $content_type\r\n" . "Content-Range: bytes $start - $end / $file_length\r\n" . "\r\n"; $content_length += strlen($boundary_header) + ($end - $start + 1); $boundaries[] = $boundary_header; } // 增加结束边界 $boundary_footer = "\r\n--$boundary--"; $content_length += strlen($boundary_footer); $boundaries[] = $boundary_footer; // 去除第一个边界中多余的 \r\n $boundaries[0] = substr($boundaries[0], 2); $content_length -= 2; // 改为特殊的多部分内容类型(Content-Type) $content_type = "multipart/byteranges; boundary= $boundary"; }}else{ // 发送整个文件 // 设置变量, 就好像这是从 Range 首部抽取的 $start = 0; $end = $file_length - 1; $offset = array($start, $end); $offsets = array($offset); $content_length = $file_length; $boundaries = array(0 => '', 1 => '');}// 指出得到的是什么header("Content-Type: $content_type"); // 前面已经指定过了,为什么这里还要指定?header("Content-Length: $content_length");// 提供给用户$handle = fopen($file, 'r');if ($handle){ $offsets_count = count($offsets); // 输出各个定界符和文件适当的部分 for($i = 0; $i < $offsets_count; $i++) { echo $boundaries[$i]; list($start, $end) = $offsets[$i]; send_range($handle, $start, $end); } // 结束边界 echo $boundaries[$i - 1]; fclose($handle);}else{ error_log("Error: fopen() fail.");}// 在文件中移动适当的位置// 按块输出所请求的部分function send_range($handle, $start, $end){ $line_length = 4096; // 魔法数 if(fseek($handle, $start) === -1) { error_log("Error:fseek() fail."); } $left_to_read = $end - $start + 1; do{ $length = min($line_length, $left_to_read); if (($buffer = fread($handle, $length)) !== false) { echo $buffer; } else { error_log("Error: fread() fail."); } }while($left_to_read -= $length);}// 发送首部失败function send_error($file_length){ http_response_code(416); header("Content-Range: bytes */$file_length");}// 将一个偏移量转换为开始和结束位置,如果偏移量是非法的返回 falsefunction parse_offset($range, $file_length){ /* * 规范: “字节范围(byte-range-spec)中的首字节位置(first-byte-pos)值指定了范围中第一个字节的字节偏移量” * "末字节位置(last-byte-pos)值指定了范围中最后一个字节的字节偏移量,也就是说,指定的字节位置包含在范围内" */ list($start, $end) = explode('-', $range); /* * */ if ($start === '') { if ($end === '' || $end === 0) { return false; } else { /* * 规范: "如果实体比指定的后缀长度(suffix-length)短,则使用整个实体体(entity-body)" */ $start = max(0, $file_length - $end); $end = $file_length - 1; } } else { /* * 规范: "如果没有提供末字节位置(last-byte-pos)值,或者如果这个值大于或等于实体体的当前长度,末字节位置则等于实体体当前长度减1" */ if ($end === '' || $end > $file_length - 1) { $end = $file_length - 1; } /* * 规范: "如果提供了末字节位置值,它必须大于或等于字节阀内规范中的首字节,否则在语法中就是不合法的" * */ if ($start > $end) { return false; } } return array($start, $end);}// 生成一个随机字符串来分割响应中的各个部分function str_rand($length = 32, $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'){ if ( ! is_int($length) || $length < 0) { return false; } $characters_length = strlen($characters) - 1; $string = ''; for($i = $length; $i > 0; $i--) { $string .= $characters[mt_rand(0, $characters_length)]; } return $string;}
0 0
- HTTP Range
- Http Range
- HTTP Range
- HTTP RANGE
- HTTP range
- HTTP Range
- HTTP RANGE
- http range
- http range
- Http头 Range、Content-Range
- http协议-- Range、If-Range
- Http头 Range、Content-Range
- HTTP Range头域
- HTTP Range说明
- HTTP Range头域
- iOS HTTP Range 使用
- HTTP之Range理解
- http range头
- Windows10 多个软件未响应(暴风影音,QQ,优酷,Edge等)
- HDU2730_Painter_贪心
- 深入javascript原型
- Linux IO调度
- 跨域访问-预请求及跨域常见问题
- http range
- poj 3087 shuffle's up bfs
- 1分钟实现“延迟消息”功能(58沈剑)
- Maven-关于在环境变量中配置两个maven的问题
- 单例模式汇总
- <a>超文本标签
- Vim高级用户3大技巧
- 浅谈数据结构——赫夫曼数
- Php开发ZendStudio常用快捷键大全