资讯专栏INFORMATION COLUMN

浅谈node.js中的stream(流)

elliott_hu / 1286人阅读

摘要:在可读流事件里我们就必须调用方法。当一个对象就意味着我们想发出信号这个流没有更多数据了自定义可写流为了实现可写流,我们需要使用流模块中的构造函数。我们只需给构造函数传递一些选项并创建一个对象。

前言

什么是流呢?看字面意思,我们可能会想起生活中的水流,电流。
但是流不是水也不是电,它只是描述水和电的流动;所以说流是抽象的。
在node.js中流是一个抽象接口,它不关心文件内容,只关注是否从文件中读到了数据,以及读到数据之后的处理,接着看:

1.流的概念

流是一组有序的,有起点和终点的字节数据传输手段

它不关心文件的整体内容,只关注是否从文件中读到了数据;以及读到数据之后的处理

流是一个抽象接口,被node中的很多对象所实现。比如HTTP服务器request和response对象都是流,TCP服务器中的socket也是流。

看看官网的介绍:

这里说了“所有的流都是EventEmitter的实例” 所以流继承了EventEmitter类。再来看流的类型:

2.流的类型

Reacable-可读的流(例如fs.createReadStream())

Writrable-可写的流(例如fs.createWriteStream())

Duplex-可读写的流(例如net.Socket)

Transform-在读写的过程中可以修改和变换数据的Duplex流(例如zlib.createDuplex())

3.流的数据模式

流中的数据有两种模式,二进制模式和对象模式.

二进制模式, 每个分块都是buffer或者string对象.

对象模式, 流内部处理的是一系列普通对象.

注意:
所有使用 Node.js API 创建的流对象都只能操作 strings 和 Buffer对象。但是,通过一些第三方流的实现,你依然能够处理其它类型的 JavaScript 值 (除了 null,它在流处理中有特殊意义)。 这些流被认为是工作在 “对象模式”(object mode)。 在创建流的实例时,可以通过 objectMode 选项使流的实例切换到对象模式。试图将已经存在的流切换到对象模式是不安全的。
说了那么多,现在开始写流:
4.可读流(createReadStream) 4.1 创建可读流

这里说说流程:

首先可读流会打开文件,触发open事件

接着开始读取,疯狂的触发data事件

然后读完了,触发end事件

最后因为设置了autoClose为true,自动关闭文件触发close事件

可以看到,data事件不断的被触发,当我们想读一下停一下时怎么办呢?
这里就需要聊聊可读流的两种模式:

4.2 可读流的两种模式

可读流事实上工作在下面两种模式之一:flowing 和 paused

在 flowing 模式下, 可读流自动从系统底层读取数据,并通过 EventEmitter 接口的事件尽快将数据提供给应用。

在 paused 模式下,必须显式调用 stream.read() 方法来从流中读取数据片段。

初始工作模式为 paused 的 Readable 流,可以通过下面三种途径切换到 flowing 模式:

1. 监听 "data" 事件
2. 调用 stream.resume() 方法

3.调用 stream.pipe() 方法将数据发送到 Writable
注意:
如果 Readable 切换到 flowing 模式,且没有消费者处理流中的数据,这些数据将会丢失。 比如, 调用了 readable.resume() 方法却没有监听 "data" 事件,或是取消了 "data" 事件监听,就有可能出现这种情况

在 paused 模式下,必须显式调用 stream.read() 方法来从流中读取数据片段。
在可读流"readable"事件里我们就必须调用stream.read()方法。

4.3 可读流"readable"事件

这里需要明白三点:
先创建一个1.txt

1.当我只要创建一个流 就会先把缓存区 填满,等待着你自己消费
2.当你消费小于 最高水位线时 会自动添加highWaterMark这么多数据

3.如果当前缓存区被清空后会再次触发readable事件

4.4 readable原理图

用Readable创建对象readable后,便得到了一个可读流。

如果实现_read方法,就将流连接到一个底层数据源。

流通过调用_read向底层请求数据,底层再调用流的push方法将需要的数据传递过来。

当readable连接了数据源后,下游便可以调用readable.read(n)向流请求数据,同时监听readable的data事件来接收取到的数据。

5.可写流(createWriteStream) 5.1 创建可写流 5.1.1 write方法

5.1.2 end方法
表明接下来没有数据要被写入 Writable 通过传入可选的 chunk 和 encoding 参数,可以在关闭流之前再写入一段数据 如果传入了可选的 callback 函数,它将作为 "finish" 事件的回调函数

5.1.3 drain方法

drain事件的触发条件,必须满足两个条件:
1.当前缓存区满了,不能再写了
2.缓存区满了后被清空了,才会触发drain事件

6.pipe方法(管道)

我们在开发中可能会遇到,要把可读流读出的数据需要放到可写流中去写入到文件里面,这时就可以用pipe方法

6.1 pipe的原理

pipe方法的原理很简单,就是读一点,写一点,上代码

let fs = require("fs");
let ws = fs.createWriteStream("./2.txt");
let rs = fs.createReadStream("./1.txt");
rs.on("data", data => {
    var flag = ws.write(data);
    if(!flag)
    rs.pause();
});
ws.on("drain", () => {
    rs.resume();
});
rs.on("end",  () => {
    ws.end();
});
6.2 pipe的用法
let from = fs.createReadStream("./1.txt");
let to = fs.createWriteStream("./2.txt");
from.pipe(to);
6.3 unpipe方法

readable.unpipe()方法将之前通过stream.pipe()方法绑定的流分离

let fs = require("fs");
let from = fs.createReadStream("./1.txt");
let to = fs.createWriteStream("./2.txt");
from.pipe(to);
setTimeout(() => {
    console.log("关闭向2.txt的写入");
    from.unpipe(writable);
    console.log("手动关闭可写流");
    to.end();
}, 1000);
7.自定义流

我们可以引入stream模块,想实现什么流 就继承这个流。

7.1 自定义可读流

我们可以直接把供使用的数据push出去。

当push一个null对象就意味着我们想发出信号——这个流没有更多数据了

7.2 自定义可写流

为了实现可写流,我们需要使用流模块中的Writable构造函数。 我们只需给Writable构造函数传递一些选项并创建一个对象。唯一需要的选项是write函数,该函数揭露数据块要往哪里写。

7.3 实现双工流(duplex)

有了双工流,我们可以在同一个对象上同时实现可读和可写,就好像同时继承这两个接口。 重要的是双工流的可读性和可写性操作完全独立于彼此。
net中的Socket就是一个duplex双工流

8.结束语

说到这里,我想大家应该大致了解了node.js里面的流。
之前说过在node里流还是很重要的,http里的request和response都是流。
在下一篇文章我会写一个readStream和writeStream的简单实现。
本人水平有限,有错误的地方希望指出。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93907.html

相关文章

  • Node.js操作实践

    摘要:事件的触发频次同样是由实现者决定,譬如在进行文件读取时,可能每行都会触发一次而在请求处理时,可能数的数据才会触发一次。如果有参数传入,它会让可读流停止流向某个特定的目的地,否则,它会移除所有目的地。 showImg(https://segmentfault.com/img/remote/1460000016328758?w=1967&h=821); 本文节选自 Node.js Chea...

    chaos_G 评论0 收藏0
  • 通过源码解析 Node.js 中导(pipe)的实现

    摘要:回调函数中检测该次写入是否被缓冲,若是,触发事件。若目标可写流表示该写入操作需要进行缓冲,则立刻将源可读流切换至暂停模式。监听源可读流的事件,相应地结束目标可写流。 在Node.js中,流(Stream)是其众多原生对象的基类,它对处理潜在的大文件提供了支持,也抽象了一些场景下的数据处理和传递。在它对外暴露的接口中,最为神奇的,莫过于导流(pipe)方法了。鉴于近期自己正在阅读Node...

    defcon 评论0 收藏0
  • 使用 Node.js 中的写工具时的两点小 tips

    摘要:中的流十分强大,它对处理潜在的大文件提供了支持,也抽象了一些场景下的数据处理和传递。本文将会提供两个在编写基于流的工具时,私以为有些用的两个。 Node.js中的流十分强大,它对处理潜在的大文件提供了支持,也抽象了一些场景下的数据处理和传递。正因为它如此好用,所以在实战中我们常常基于它来编写一些工具 函数/库 ,但往往又由于自己对流的某些特性的疏忽,导致写出的 函数/库 在一些情况会达...

    fizz 评论0 收藏0
  • Node.js学习之路08——fs文件系统之stream的基本介绍

    摘要:中各种用于读取数据的对象对象描述用于读取文件代表客户端请求或服务器端响应代表一个端口对象用于创建子进程的标准输出流。如果子进程和父进程共享输入输出流,则子进程的标准输出流被废弃用于创建子进程的标准错误输出流。 9. stream流 fs模块中集中文件读写方法的区别 用途 使用异步方式 使用同步方式 将文件完整读入缓存区 readFile readFileSync 将文件部...

    BoYang 评论0 收藏0
  • [转]nodejs Stream使用手册

    摘要:方法也可以接收一个参数表示数据请求着请求的数据大小,但是可读流可以根据需要忽略这个参数。读取数据大部分情况下我们只要简单的使用方法将可读流的数据重定向到另外形式的流,但是在某些情况下也许直接从可读流中读取数据更有用。 介绍本文介绍了使用 node.js streams 开发程序的基本方法。 We should have some ways of connecting programs ...

    luffyZh 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<