摘要:如獲取字節(jié)的數(shù)據(jù)和到結(jié)尾的數(shù)據(jù)該頭部指定了響應(yīng)的數(shù)據(jù)的內(nèi)容范圍,語法格式如下說明數(shù)據(jù)區(qū)間所采用的單位。
實(shí)現(xiàn)一個(gè)視頻播放的功能,以及對(duì)大文件的下載操作等等都避不開一個(gè)點(diǎn):獲取文件任意位置的數(shù)據(jù),如果說我們單純的通過 echo file-content 的方式只能用于文件下載,如果視頻文件用于播放中,則難以處理,具體表現(xiàn)則為視頻播放的時(shí)候無法調(diào)整進(jìn)度條,而且如果是視頻網(wǎng)站,對(duì)于視頻只采用放在某個(gè)可以直接訪問的目錄上,那么這個(gè)視頻也就相當(dāng)于公開了,對(duì)于什么 VIP 什么的也就無從說起,本篇文章將 Range,來提供視頻播放、斷點(diǎn)續(xù)傳、多線程下載的技術(shù)依賴實(shí)現(xiàn)
RangeHTTP協(xié)議中,支持以 Range 的形式指定獲取資源的特定偏移的數(shù)據(jù),語法格式如下,具體參考 Range: MDN:
Range:= - Range: = - Range: = - , - Range: = - , - , -
如: 獲取 0-100 字節(jié)的數(shù)據(jù)和120到結(jié)尾的數(shù)據(jù)
Range: bytes=0-100,120-Content-Range
該頭部指定了響應(yīng)的數(shù)據(jù)的內(nèi)容范圍,語法格式如下:
Content-Range:- / Content-Range: - /* Content-Range: */
說明:
例如:
Content-Range: bytes 200-1000/67589多Range響應(yīng)
目測(cè)在網(wǎng)絡(luò)上面的都沒有說到,但是HTTP協(xié)議支持多Range,具體返回內(nèi)容信息格式如下:
GET http://suda.dev.dx/file HTTP/1.1 Host: suda.dev.dx Connection: keep-alive Accept-Encoding: identity;q=1, *;q=0 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3679.0 Safari/537.36 Accept: */* Referer: http://test.dev.dx/video.html Accept-Language: zh-CN,zh;q=0.9 Cookie: php_session=8eec314af63d994c2eeb1baca7487332 Range: bytes=0-1,2-3 HTTP/1.1 206 Partial Content Date: Sun, 10 Mar 2019 09:36:59 GMT Server: Apache/2.4.23 (Win32) OpenSSL/1.0.2j mod_fcgid/2.3.9 X-Powered-By: PHP/7.2.1 Accept-Ranges: bytes Content-Length: 220 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: multipart/byteranges; boundary=multiple_range_ss6bBSB6IlLi0YPpP8rK3g== --multiple_range_ss6bBSB6IlLi0YPpP8rK3g== Content-Type: video/mp4 Content-Range: bytes 0-1/132006090 <...somedata...> --multiple_range_ss6bBSB6IlLi0YPpP8rK3g== Content-Type: video/mp4 Content-Range: bytes 2-3/132006090 <...somedata...>Accept-Rangs
服務(wù)器響應(yīng),告訴瀏覽器是否支持 Range,
語法:
Accept-Ranges: bytes Accept-Ranges: none
none
不支持任何范圍請(qǐng)求單位,由于其等同于沒有返回此頭部,因此很少使用。不過一些瀏覽器,比如IE9,會(huì)依據(jù)該頭部去禁用或者移除下載管理器的暫停按鈕。
bytes
范圍請(qǐng)求的單位是 bytes (字節(jié))
本實(shí)現(xiàn)代碼可以簡(jiǎn)單理解為偽代碼,部分依賴沒有給出,Swoole 環(huán)境下修改一下即可使用。
使用代碼:onRequest($request, $response); } }依賴代碼:
file = $file instanceof SplFileObject? $file : new SplFileObject($file); $this->mime = MimeType::getMimeType($this->file->getExtension()); } /** * 處理文件請(qǐng)求 * * @param sudaframeworkRequest $request * @param sudaframeworkResponse $response * @return void */ public function onRequest(Request $request, Response $response) { $ranges = $this->getRanges($request); $response->setHeader("accept-ranges", "bytes"); if ($ranges === false || $request->getMethod() !== "GET") { $response->status(400); } elseif ($ranges === null) { $response->sendFile($this->file->getRealPath()); } elseif (count($ranges) === 1) { $response->status(206); $range = $ranges[0]; $response->setHeader("content-type", $this->mime); $response->setHeader("content-range", $this->getRangeHeader($range)); $this->sendFileByRange($response, $range); } else { $response->status(206); $this->sendMultipleFileByRange($response, $ranges); } } /** * 發(fā)送多Range * * @param sudaframeworkResponse $response * @param array $ranges * @return void */ protected function sendMultipleFileByRange(Response $response, array $ranges) { $separates = "multiple_range_".base64_encode(md5(uniqid(), true)); $response->setHeader("content-type", "multipart/byteranges; boundary=".$separates); foreach ($ranges as $range) { $response->write("--".$separates." "); $this->sendMultipleRangePart($response, $range); $this->sendFileByRange($response, $range); $response->write(" "); } } /** * 發(fā)送范圍數(shù)據(jù) * * @param sudaframeworkResponse $response * @param array $range * @return void */ protected function sendFileByRange(Response $response, array $range) { $response->write(new DataStream($this->file->getRealPath(), $range["start"], $range["end"] - $range["start"] + 1)); } /** * 獲取Range描述 * * @param sudaframeworkRequest $request * @return array|bool|null */ protected function getRanges(Request $request) { $ranges = $this->parseRangeHeader($request); if (is_array($ranges)) { return $this->parseRanges($ranges); } elseif ($ranges === false) { return false; } return null; } /** * 寫Range頭 * * @param sudaframeworkResponse $response * @param array $range * @return void */ protected function sendMultipleRangePart(Response $response, array $range) { $response->write("Content-Type: ".$this->mime." "); $response->write("Content-Range: ".$this->getRangeHeader($range) ." "); } /** * 生成Range頭 * * @param array $range * @return string */ protected function getRangeHeader(array $range):string { return sprintf("bytes %d-%d/%d", $range["start"], $range["end"], $this->file->getSize()); } /** * 獲取Range描述 * * @param sudaframeworkRequest $request * @return array|bool|null */ protected function parseRangeHeader(Request $request) { $range = $request->getHeader("range", null); if (is_string($range)) { $range = trim($range); if (strpos($range, "bytes=") !== 0) { return false; } $rangesFrom = substr($range, strlen("bytes=")); return explode(",", $rangesFrom); } return null; } /** * 處理范圍 * * @param array $ranges * @return array|bool */ protected function parseRanges(array $ranges) { $range = []; foreach ($ranges as $value) { if (($r = $this->parseRange($value)) !== null) { $range[] = $r; } else { return false; } } return $range; } /** * 處理Range * * @param string $range * @return array */ protected function parseRange(string $range):?array { $range = trim($range); if (strrpos($range, "-") === strlen($range) - 1) { return [ "start" => intval( trim($range, "-")), "end" => $this->file->getSize() - 1, ]; } elseif (strpos($range, "-") !== false) { list($start, $end) = explode("-", $range, 2); return ["start" => intval($start) , "end" => intval($end) ]; } return null; } }參考文獻(xiàn)
https://tools.ietf.org/html/r...
https://tools.ietf.org/html/r...
https://developer.mozilla.org...
https://developer.mozilla.org...
完整代碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/30972.html
摘要:練習(xí)項(xiàng)目備選清單文件下載器功能概要設(shè)計(jì)實(shí)現(xiàn)新建下載功能以為基礎(chǔ)給出下載鏈接可以啟動(dòng)下載任務(wù)實(shí)現(xiàn)局域網(wǎng)內(nèi)下載傳輸文件以單線程下載方式實(shí)現(xiàn)附加功能支持?jǐn)帱c(diǎn)續(xù)傳實(shí)現(xiàn)多線程下載實(shí)現(xiàn)下載參考技術(shù)套接字編程多線程編程音視頻播放器功能概要設(shè)計(jì)實(shí)現(xiàn)播放常見 練習(xí)項(xiàng)目備選清單 Utilities 1. 文件下載器 功能概要設(shè)計(jì): 實(shí)現(xiàn)新建下載功能(以ftp為基礎(chǔ)) 給出下載鏈接可以啟動(dòng)下載任務(wù) 實(shí)現(xiàn)局...
摘要:練習(xí)項(xiàng)目備選清單文件下載器功能概要設(shè)計(jì)實(shí)現(xiàn)新建下載功能以為基礎(chǔ)給出下載鏈接可以啟動(dòng)下載任務(wù)實(shí)現(xiàn)局域網(wǎng)內(nèi)下載傳輸文件以單線程下載方式實(shí)現(xiàn)附加功能支持?jǐn)帱c(diǎn)續(xù)傳實(shí)現(xiàn)多線程下載實(shí)現(xiàn)下載參考技術(shù)套接字編程多線程編程音視頻播放器功能概要設(shè)計(jì)實(shí)現(xiàn)播放常見 練習(xí)項(xiàng)目備選清單 Utilities 1. 文件下載器 功能概要設(shè)計(jì): 實(shí)現(xiàn)新建下載功能(以ftp為基礎(chǔ)) 給出下載鏈接可以啟動(dòng)下載任務(wù) 實(shí)現(xiàn)局...
摘要:怎么支持?jǐn)帱c(diǎn)續(xù)傳的協(xié)議中默認(rèn)支持獲取文件的部分內(nèi)容,這其中主要是通過頭部的兩個(gè)參數(shù)和來實(shí)現(xiàn)的。我們?cè)谒⒁幌旅嬖囶}的時(shí)候,有時(shí)候會(huì)看到一些大廠會(huì)問關(guān)于斷點(diǎn)續(xù)傳的原理,那么今天在這里從 HTTP 斷點(diǎn)續(xù)傳知識(shí)和 Android 中如何實(shí)現(xiàn)斷點(diǎn)續(xù)傳的思路來做一個(gè)關(guān)于 Android 斷點(diǎn)續(xù)傳原理的總結(jié)。 Http 斷點(diǎn)續(xù)傳知識(shí)點(diǎn) 什么是斷點(diǎn)續(xù)傳 指的是在上傳/下載時(shí),將任務(wù)(一個(gè)文件或壓縮包)人為...
摘要:擴(kuò)展支持多用戶并發(fā)訪問與線程池。項(xiàng)目請(qǐng)見初學(xué)網(wǎng)絡(luò)編程之服務(wù)器。不允許超過磁盤配額。該文件是一個(gè)使用模塊編寫的線程池類。這一步就做到了線程池的作用。 對(duì)MYFTP項(xiàng)目進(jìn)行升級(jí)。擴(kuò)展支持多用戶并發(fā)訪問與線程池。MYFTP項(xiàng)目請(qǐng)見python初學(xué)——網(wǎng)絡(luò)編程之FTP服務(wù)器。 擴(kuò)展需求 1.在之前開發(fā)的FTP基礎(chǔ)上,開發(fā)支持多并發(fā)的功能2.不能使用SocketServer模塊,必須自己實(shí)現(xiàn)多線...
閱讀 5472·2021-09-07 09:58
閱讀 870·2019-08-30 15:55
閱讀 3167·2019-08-30 15:55
閱讀 1051·2019-08-30 15:53
閱讀 1649·2019-08-29 12:57
閱讀 2002·2019-08-26 13:46
閱讀 648·2019-08-26 11:00
閱讀 3749·2019-08-23 15:42