我正在尝试修改自定义Web服务器应用程序以使用
HTML5视频.
它提供HTML5页面,其中包含基本的< video>标签,然后它需要处理实际内容的请求.
到目前为止,我能让它工作的唯一方法是将整个视频文件加载到内存中,然后在一个响应中将其发回.这不是一个实用的选择.我想逐件服务:发送回来,比方说,100 kb,等待浏览器请求更多.
我看到一个带有以下标题的请求:
http_version = 1.1 request_method = GET Host = ###.###.###.###:## User-Agent = Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0 Accept = video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5 Accept-Language = en-US,en;q=0.5 Connection = keep-alive Range = bytes=0-
HTTP/1.1 206 Partial content Content-Type: video/mp4 Content-Range: bytes 0-99999 / 232725251 Content-Length: 100000
我得到了一些GET请求,如下所示
Cache-Control = no-cache Connection = Keep-Alive Pragma = getIfoFileURI.dlna.org Accept = */* User-Agent = NSPlayer/12.00.7601.17514 WMFSDK/12.00.7601.17514 GetContentFeatures.DLNA.ORG = 1 Host = ###.###.###.###:##
(没有任何迹象表明浏览器需要文件的任何特定部分.)无论我发送回浏览器,视频都不会播放.
如上所述,如果我尝试在同一HTTP数据包中一次发送整个230 MB文件,则相同的视频将正常播放.
有没有办法让这一切都能通过部分内容请求很好地工作?我正在使用Firefox进行测试,但它最终需要与所有浏览器一起使用.
解决方法
我知道这是一个老问题,但是如果它有帮助你可以尝试我们在代码库中使用的以下“模型”.
class Model_DownloadableFile { private $full_path; function __construct($full_path) { $this->full_path = $full_path; } public function get_full_path() { return $this->full_path; } // Function borrowed from (been cleaned up and modified slightly): https://stackoverflow.com/questions/157318/resumable-downloads-when-using-PHP-to-send-the-file/4451376#4451376 // Allows for resuming paused downloads etc public function download_file_in_browser() { // Avoid sending unexpected errors to the client - we should be serving a file,// we don't want to corrupt the data we send @error_reporting(0); // Make sure the files exists,otherwise we are wasting our time if (!file_exists($this->full_path)) { header('HTTP/1.1 404 Not Found'); exit; } // Get the 'Range' header if one was sent if (isset($_SERVER['HTTP_RANGE'])) { $range = $_SERVER['HTTP_RANGE']; // IIS/Some Apache versions } else if ($apache = apache_request_headers()) { // Try Apache again $headers = array(); foreach ($apache as $header => $val) { $headers[strtolower($header)] = $val; } if (isset($headers['range'])) { $range = $headers['range']; } else { $range = false; // We can't get the header/there isn't one set } } else { $range = false; // We can't get the header/there isn't one set } // Get the data range requested (if any) $filesize = filesize($this->full_path); $length = $filesize; if ($range) { $partial = true; list($param,$range) = explode('=',$range); if (strtolower(trim($param)) != 'bytes') { // Bad request - range unit is not 'bytes' header("HTTP/1.1 400 Invalid Request"); exit; } $range = explode(',',$range); $range = explode('-',$range[0]); // We only deal with the first requested range if (count($range) != 2) { // Bad request - 'bytes' parameter is not valid header("HTTP/1.1 400 Invalid Request"); exit; } if ($range[0] === '') { // First number missing,return last $range[1] bytes $end = $filesize - 1; $start = $end - intval($range[0]); } else if ($range[1] === '') { // Second number missing,return from byte $range[0] to end $start = intval($range[0]); $end = $filesize - 1; } else { // Both numbers present,return specific range $start = intval($range[0]); $end = intval($range[1]); if ($end >= $filesize || (!$start && (!$end || $end == ($filesize - 1)))) { $partial = false; } // Invalid range/whole file specified,return whole file } $length = $end - $start + 1; } else { $partial = false; // No range requested } // Determine the content type $finfo = finfo_open(FILEINFO_MIME_TYPE); $contenttype = finfo_file($finfo,$this->full_path); finfo_close($finfo); // Send standard headers header("Content-Type: $contenttype"); header("Content-Length: $length"); header('Content-Disposition: attachment; filename="' . basename($this->full_path) . '"'); header('Accept-Ranges: bytes'); // if requested,send extra headers and part of file... if ($partial) { header('HTTP/1.1 206 Partial Content'); header("Content-Range: bytes $start-$end/$filesize"); if (!$fp = fopen($this->full_path,'r')) { // Error out if we can't read the file header("HTTP/1.1 500 Internal Server Error"); exit; } if ($start) { fseek($fp,$start); } while ($length) { // Read in blocks of 8KB so we don't chew up memory on the server $read = ($length > 8192) ? 8192 : $length; $length -= $read; print(fread($fp,$read)); } fclose($fp); } else { readfile($this->full_path); // ...otherwise just send the whole file } // Exit here to avoid accidentally sending extra content on the end of the file exit; } }
然后你像这样使用它:
(new Model_DownloadableFile('FULL/PATH/TO/FILE'))->download_file_in_browser();