问题记录
项目使用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)node.js 文件服务
前端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>"; });html 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的默认高度和宽度