文件下載是WEB網(wǎng)站提供的最基本服務(wù),,然而你知道HTTP的斷點(diǎn)續(xù)傳是怎么實(shí)現(xiàn)的嗎? 背景 這兩天在實(shí)現(xiàn)一個(gè)基于HTML5在線音視頻播放,,由于文件是存放于企業(yè)網(wǎng)盤中的,,HTTP不可達(dá),因此需要用程序來實(shí)現(xiàn)文件的讀取和HTTP協(xié)議的下載,。
用Java實(shí)現(xiàn)文件下載也不用多說了,,讀取文件,通過二進(jìn)制流的方式往response里寫就行了,。H5播放器調(diào)用也能進(jìn)行播放了,;然而當(dāng)我控制進(jìn)度的前進(jìn)和后退時(shí),問題來了,,居然一點(diǎn)效果都沒有,!沒有快進(jìn)播放器還叫播放器嗎? 分析 首先看到播放器無法取得音視頻文件的時(shí)間長(zhǎng)度,,很自然想到Content-Length屬性,,后臺(tái)通過file.length()取得文件長(zhǎng)度并設(shè)置到Content-Length上(代碼如下),,前臺(tái)播放器里可以顯示音視頻的長(zhǎng)度了,并且可以快進(jìn)了,;然而當(dāng)我快退的時(shí)候,,還是無效,同時(shí)后臺(tái)報(bào)錯(cuò),。 response.addHeader('Content-Length', file.length()); 換了一個(gè)HTTP文件進(jìn)行比較測(cè)試,,發(fā)現(xiàn)直接HTTP訪問的能夠正常快進(jìn)快退,。仔細(xì)分析兩者的request和response頭,,發(fā)現(xiàn)了區(qū)別,請(qǐng)求參數(shù) 多了如下圖所示屬性,,該屬性表名需要從服務(wù)端獲取的資源范圍,,默認(rèn)從第一個(gè)字節(jié)開始取,快進(jìn)快退實(shí)際上就是通過指定這個(gè) Range屬性來確定你所期望的起始點(diǎn),。 然而這個(gè)屬性是在請(qǐng)求頭上的,,客戶端又是怎么知道要添加這個(gè)屬性呢?繼續(xù)尋找發(fā)現(xiàn)了 Accept-Ranges這個(gè)屬性,,屬性值是bytes,,其表明是否接受獲取其某個(gè)實(shí)體的一部分(比如文件的一部分)的請(qǐng)求。bytes:表示接受,,none:表示不接受,。與此對(duì)應(yīng)的response中另外一個(gè)屬性 Content-Range,其表名該響應(yīng)包含的部分對(duì)象為整個(gè)對(duì)象的哪個(gè)部分,。完整響應(yīng)頭如下: 解決 根據(jù)上面的分析,,我們就知道在服務(wù)端該怎么處理了,首先在響應(yīng)頭上添加Accept-Ranges,。 response.setHeader('Accept-Ranges', 'bytes'); 接著判斷request中是否存在Range屬性,,即是否指定的起點(diǎn),若存在,,則通過stream的skip直接跳到目標(biāo)起點(diǎn),,最后添加Content-Range屬性表名當(dāng)前塊的起始和結(jié)束,完整代碼如下: stream = new FileInputStream(file);if(request.getHeader('Range') != null) //客戶端請(qǐng)求的下載的文件塊的開始字節(jié) { //從請(qǐng)求中得到開始的字節(jié) //請(qǐng)求的格式是: //Range: bytes=[文件塊的開始字節(jié)]- String range = StringUtils.substringBetween(request.getHeader('Range'), 'bytes=', '-'); long start = Long.parseLong(range); //下載的文件(或塊)長(zhǎng)度 //響應(yīng)的格式是: //Content-Length: [文件的總大小] - [客戶端請(qǐng)求的下載的文件塊的開始字節(jié)] response.setHeader('Content-Length', String.valueOf(fileSize - start)); if (start != 0) { //要設(shè)置狀態(tài) //響應(yīng)的格式是: //HTTP/1.1 206 Partial Content response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);//206 //不是從最開始下載, //響應(yīng)的格式是: //Content-Range: bytes [文件塊的開始字節(jié)]-[文件的總大小 - 1]/[文件的總大小] response.setHeader('Content-Range','bytes ' + start + '-' + String.valueOf(fileSize - 1) + '/' + String.valueOf(fileSize)); stream.skip(start); } } responseBinaryStream(response, this.getContentType(FilenameUtils.getExtension(fileName)), stream);
|