node视频流简单优化

之前在小组内做完HLS的分享之后,正好有一个视频相关的线上demo,视频格式是MP4,大概18M,接口只能一次将整个视频资源获取下来之后才能播放,这样就会有一个空白等待期,也不支持播放进度拖拽跳转,同组的同学就让我对这个演示视频进行优化。
查阅了一些资料,也尝试了一些视频播放相关的框架之后,接口不支持流媒体,前端还是需要等待整个资源下载下来之后,才能做进度拖拽,跳转播放等功能,于是我的方案就是在node层拦截视频请求接口,利用文件流实现流媒体。
ps:有同学给我说,不需要在node转发,也可以实现,暂时还没有找到相关方案。

HTTP的断点续传

文件上传下载时,记录上一次上传下载的位置,再从标记位置继续传输,或者多线程下载,根据标记可按需获取。
在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。一般断点下载时才用到 Range 和 Content-Range 实体头。
Range
请求资源的部分内容(不包括响应头的大小),单位是byte,即字节,从0开始,如果服务器能够正常响应的话,服务器会返回 206 Partial Content 的状态码及说明,如果不能处理这种Range的话,就会返回整个资源以及响应状态码为 200 OK,(这个要注意,要分段下载时,要先判断这个)用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:
Range: bytes=start-end
eg:
Range: bytes=10- :第10个字节及最后个字节的数据
Range: bytes=40-100 :第40个字节到第100个字节之间的数据

响应头Content-Range
Content-Range: bytes 0-100/5000
这个表示相应了0到100个字节的数据,共有5000个字节大小
Content-Length: 5000
总文件大小

node中fs模块

fs.statSync(path, callback);
该方法用于检测文件的状态,可以借此来判断某个文件是否存在。path参数传入该文件的绝对物理路径,callback回调函数有两个参数err和stats。其中err为错误信息参数,stats为一个文件状态对象。
方法返回一个数组

Stats {
    dev: 2114,
    ino: 48064969,
    mode: 33188,
    nlink: 1,
    uid: 85,
    gid: 100,
    rdev: 0,
    size: 527,
    blksize: 4096,
    blocks: 8,
    atimeMs: 1318289051000.1,
    mtimeMs: 1318289051000.1,
    ctimeMs: 1318289051000.1,
    birthtimeMs: 1318289051000.1,
    atime: Mon, 10 Oct 2011 23:24:11 GMT,
    mtime: Mon, 10 Oct 2011 23:24:11 GMT,
    ctime: Mon, 10 Oct 2011 23:24:11 GMT,
    birthtime: Mon, 10 Oct 2011 23:24:11 GMT
}

fs.createReadStream(path, [options])创建文件可读流,options可以指定读取文件参数,包括start,end,指定读取特定位置文件数据
完整代码

const express = require('express');
const path = require('path');
const app = express();
const fs = require('fs');

app.get('/*.mp4$/', function(req, res) {
    let path = 'video.mp4';
    let stat = fs.statSync(path);
    let fileSize = stat.size;
    let range = req.headers.range;

    if(range) {
        //有range头才使用206状态码
        let parts = range.replace(/bytes=/, "").split("-");
        let start = parseInt(parts[0], 10);
        let end = parts[1] ? parseInt(parts[1], 10) : start + 999999;

        // end 在最后取值为 fileSize - 1 
        end = end > fileSize - 1 ? fileSize - 1 : end;
        let chunksize = (end - start) + 1;
        let file = fs.createReadStream(path, {start, end});
        let head = {
            'Content-Range': `bytes ${start}-${end}/${fileSize}`,
            'Accept-Ranges': 'bytes',
            'Content-Length': chunksize,
            'Content-Type': 'video/mp4'
        };
        res.writeHead(206, head);
        file.pipe(res);
    }else {
        let head = {
            'Content-Length': fileSize,
            'Content-Type': 'video/mp4',
        };
        res.writeHead(200, head);
        fs.createReadStream(path).pipe(res);
    }
});

app.use(express.static(path.join(__dirname, 'output')));

var server = app.listen(3000, function() {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

你可能感兴趣的:(node视频流简单优化)