問題記錄
項目使用node.js寫的文件服務器,前端查看圖片都沒問題,但是查看視頻時,windows系統和andriod系統手機都可以,但是ios不行
后台發現是ios向服務器請求視頻的特性決定的
問題解決
一開始我的node.js文件解析是這樣的
function readFile(filePath, contentType){ response.writeHead(200, { "content-type": contentType}); //建立流對象,讀文件 var stream = fs.createReadStream(filePath); //錯誤處理 stream.on('error', function() { response.writeHead(500, { "content-type": contentType }); response.end("<h1>500 Server Error</h1>"); }); //讀取文件 stream.pipe(response); }
按照如上支持了PC和安卓的h5播放。但是IOS端不能播放視頻。
錯誤原因就是https響應缺少字段。
1.Accept-Ranges:可接收字節
2.Content-Length:返回數據長度
3.Content-Range:返回數據的范圍
究其原因就是ios端是分段請求視頻的,在請求過程中,請求頭里會有一個‘range’字段,當這個range字段存在時,標志每次要請求的數據長度就是range.start-range.end,
那么我們每次創建數據流的時候也是只需要創建這一段之間的數據流就可以了(不可以多,也不可以少)
var stream = fs.createReadStream(realpath, { "start": range.start, "end": range.end });
safari之所以分多次請求也有深層次原因。比方說先請求0-1字節(其實是2個字節),返回的時候數據並不多,但是可以通過分析"Content-Range"來獲取文件總長度。然后分段請求,比如請求第一幀來渲染thumb nail等等。這樣做有個好處就是,只有當用戶點擊播放了才請求完整文件,對於PC還好,對於手機這類數據傳輸需要收費的設備來說,必須要節省流量。
另外在iphone上chrome也用的是apple提供的內核,導致他們的行為基本上一致。(這是蘋果的規定)。
修改之后的請求文件為:

/* * @Author: your name * @Date: 2020-01-17 14:55:29 * @LastEditTime : 2020-01-17 16:10:56 * @LastEditors : Please set LastEditors * @Description: In User Settings Edit * @FilePath: \files-test\app.js */ /** * Created by nodefx on 8/29/14. */ var path = require('path'); var fs = require('fs') var http = require('http') var url = require('url') var mime = require('./mime').types var config = require("./config"); var zlib = require("zlib") var utils = require("./utils") var port = 3011; var server = http.createServer(function(request, response){ var pathname = url.parse(request.url).pathname; var realpath=path.normalize(pathname.replace(/\.\./g, "")) realpath = path.resolve(__dirname + realpath); //var realpath = path.join("assets", path.normalize(pathname.replace(/\.\./g, ""))) console.log(realpath) var ext = path.extname(realpath) ext = ext ? ext.slice(1):"unknown" var contentType = mime[ext] || "text/plain" console.log(contentType) fs.exists(realpath, function(exists) { if (!exists) { response.writeHead(404, { 'Content-Type': 'text/plain' }) response.write("This request URL " + pathname + "was not found on this server"); response.end(); } else { response.setHeader("Content-Type",contentType); var stats = fs.statSync(realpath); if (request.headers["range"]) { console.log(request.headers["range"]) var range = utils.parseRange(request.headers["range"], stats.size); console.log(range) if (range) { response.setHeader("Content-Range", "bytes " + range.start + "-" + range.end + "/" + stats.size); response.setHeader("Content-Length", (range.end - range.start + 1)); var stream = fs.createReadStream(realpath, { "start": range.start, "end": range.end }); response.writeHead('206', "Partial Content"); stream.pipe(response); // compressHandle(raw, 206, "Partial Content"); } else { response.removeHeader("Content-Length"); response.writeHead(416, "Request Range Not Satisfiable"); response.end(); } } else { var stream = fs.createReadStream(realpath); response.writeHead('200', "Partial Content"); stream.pipe(response); // compressHandle(raw, 200, "Ok"); } } }) var compressHandle = function (raw, statusCode, reasonPhrase) { var stream = raw; var acceptEncoding = request.headers['accept-encoding'] || ""; var matched = ext.match(config.Compress.match); if (matched && acceptEncoding.match(/\bgzip\b/)) { response.setHeader("Content-Encoding", "gzip"); stream = raw.pipe(zlib.createGzip()); } else if (matched && acceptEncoding.match(/\bdeflate\b/)) { response.setHeader("Content-Encoding", "deflate"); stream = raw.pipe(zlib.createDeflate()); } response.writeHead(statusCode, reasonPhrase); stream.pipe(response); }; }) server.listen(port) console.log('app run at:'+port)
前端web頁面video播放

<div class="article-video" v-html="srcTag"></div> that.$nextTick(() => { that.srcTag = '<video style="width:100%;height:100%;" controls preload="auto">' + `<source src="${that.article.video_path}" type="video/mp4"></source>` + "您的瀏覽器不支持此視頻格式" + "</video>"; });
附上網絡上的github地址(感謝):
https://github.com/node-node/vedio_stream
https://www.zhihu.com/question/41818719
附:ios對h5播放的支持
一.對視頻格式的要求:
HTML5沒有規定瀏覽器到底應該播放哪一種格式的視頻。瀏覽器廠商可以自行選擇支持的格式。市面上幾種視頻編碼格式:vp3、Theora、vp8、H.264....;其中Theora和vp8都是基於vp3再次開發;蘋果公司使用的是H.264視頻編碼格式。H.264優點,編碼后生成的視頻文件,體積較小,畫質也不錯;蘋果公司和微軟公司,它們各自擁有一些H.264專利,所以Safari瀏覽器只支持H.264編碼格式的視頻
二.對video標簽的修改
1.Safari通過使用全屏幕播放視頻來優化iPhone或iPod touch上的較小屏幕的視頻演示 - 觸摸屏幕時出現視頻控件,所以設置video的寬高只對頁面上顯示有效,在全屏播放時會調用蘋果自帶的控制組件;但是在大屏幕設備上視頻是可以嵌套在頁面上播放的
2.蘋果出於流量損耗的考慮,禁止了那些非用戶輸入觸發的播放動作,這意味着給 video 標簽增加 preload 及 autoplay 屬性都是無效的,並且也無法使用 JS 的 play() 和 load() 方法來播放和加載視頻,除非是用戶手動觸發
3.由於視頻的視頻元數據加載之前是不知道的,所以如果未指定高度或寬度,則在運行iOS的設備上分配150 x 300的默認高度和寬度