Node.js发送视频流

Node.js中的流
Node.js的流(Stream) API 非常强大,它是处理流数据的抽象接口。流可以看成是一种数据的集合,但它并不是一下子全部读到内存里面,而是一块一块地去产生、消耗,这种方式最显而易见的好处是可以方便地处理大文件。数据流可以是可读流、可写流,实际上Node.js中的流分为4种类型 : Readable、Writable、Duplex、Transform。

Readable Stream:可读流是对可消费的数据源进行的抽象,比如fs.createReadStream
Writable Stream:可写流是对流的目的地(destination)的抽象,destination运允许数据写入,比如fs.createWriteStream
Duplex Stream:双工流是同时实现了 Readable 和 Writable 接口的流,既能写又能读。比如TCP socket
Transform Stream:交换流本质上是一种Duplex流,可以将其看成输入Writable流,输出的是Readable流,也可以称之为“通过流”(through streams)。比如zlib streams。
Node中有许多内置对象实现了Stream接口: 
 

对于TCP sockets、zlib 和 crypto 流而言,他们是Duplex Stream。

.pipe()方法
对于Readable流而言,有两种消费数据流的方式:Paused Mode 和 Flowing Mode。简单来说,Paused Mode就像是把水缸里面的水一瓢瓢舀出来,可以根据需要使用read()方法去消费数据流;Flowing Mode就像是给水缸接了根管子,水可以从高处流到低处,我们可以监听data事件得到一块数据流。

所有的Readable流默认是Paused Mode,使用resume()、pause()方法可以与 Flowing Mode 相互切换,resume()方法就像是给水缸接好管子,水自动流动;pause()方法就像移除管子,我们得手动去舀水。这种切换方式很简单,是有时候是自动发生的。 
 

当Readable流使用pipe()方法时,就相当于给数据流接上了管子,数据流会自动从上游流到下游。在使用pipe()时,需要注意的是上游是Readable,下游是Writable,即:

readableSrc.pipe(writableDest)

由于Duplex流 实现了Readable、和Writable,可以将Duplex或Transform 放在“中游”:

readableSrc .pipe(transformStream1) .pipe(transformStream2) .pipe(finalWrtitableDest)

我们知道,Node中的Server端HTTP response是Writable流,而通过fs.createReadStream读取视频数据得到的是Readable流。因此,Node的Server端可以直接使用pipe()方法将视频流发到前端:

 

/前端
<video src="/video"></video>

//服务端
router.get('/video', function(req, res, next) {

    let head = { 'Content-Type': 'video/mp4' };
    //需要设置HTTP HEAD
    res.writeHead(200, head);
    //使用pipe
    fs.createReadStream('./assets/sintel.mp4')
        .pipe(res);

});
 前端结果: 

 

可以注意到前端下载了一个html和video。

HTTP 206
HTTP/1.1 206状态码表示的是:客户端通过发送范围请求头Range获取资源的部分数据。这种请求可以将服务端文件分割成多个部分传给客户端,可用于解决网络问题以及大文件下载问题。对于一个很大的视频,就可以采用这种请求将视频流分成多个部分下载。 
需要关注的HTTP Headers有:

Range:用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:Range:(unit=first byte pos)-[last byte pos] 。如 Range:bytes=0- 表示请求服务端第0及以后bytes的数据; Range:bytes=0-999 表示0到999 bytes的数据,注意这个区间的长度是1000bytes。
Accept-Ranges:用于响应头,表明服务器支持Range请求,以及服务器所支持的单位是字节(这也是唯一可用的单位);Accept-Ranges: none 响应头表示服务器不支持范围请求。
Content-Range:用于响应头,指定整个实体中的一部分的插入位置,一般格式: Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]。如Content-Range:bytes 1000000-1999999/3332038 表示的是服务端资源总大小3332038 bytes,此次返回的是其中第1000000到1999999 bytes 的数据。
Content-Length:用于响应头,表明了响应实体的大小,它应该等于Content-Range中的 last byte pos - first byte pos + 1。
将视频流分成多个部分发给前端,只要注意控制好流的数据区间即可,服务端代码如下:
 

router.get('/video', function(req, res, next) {
    let path = './assets/sintel.mp4';
    let stat = fs.statSync(path);
    let fileSize = stat.size;
    let range = req.headers.range;

    // fileSize 3332038

    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);
    }

});
 前端结果: 

这次的视频就被分成了4个部分,每个video请求及结果如下:

 

代码地址:https://git.oschina.net/liuyaqi/node-video-stream_demo.git

参考链接: 
菜鸟教程: Node.js Stream(流) 
Node中文文档 Stream 
Node.js Streams: Everything you need to know 
RFC 2616: HTTP状态码定义 
http断点续传原理:http头 Range、Content-Range
 转自https://blog.csdn.net/liuyaqi1993/article/details/76560401

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值