nodejs的stream模块

Stream 是Node.js中最重要的组件和模式之一,在构建较复杂的系统时,通常将其拆解为功能独立的若干部分。这些部分的接口遵循一定的规范,通过某种方式相连,以共同完成较复杂的任务。nodejs的核心模块,基本上都是stream的的实例,比如process.stdout、http.clientRequest

什么是流?

  • 流是一组有序的,有起点和终点的字节数据传输手段
  • 它不关心文件的整体内容,只关注是否从文件中读到了数据,以及读到数据之后的处理
  • 流是一个抽象接口,被 Node 中的很多对象所实现。比如HTTP 服务器request和response对象都是流。

简单的理解,流就是将大块的东西,分小块依次处理。就像你需要从水龙头上接一杯水,那么当你拧开水龙头,水管就会一点点的源源不断的流出来给你。


image.png

那么流这种方式在程序当中又有什么优势呢?先看如下代码:

let fs = require('fs');
fs.readFile('./1.txt', 'utf8', function(err, data){
    // 1.txt 已经读取完成
    console.log(data);
    fs.writeFile('/2.txt', data); // 将内容写入2.txt中
});

以上两个方法是实现的功能是将1.txt文件读取到内存当中,再将它写入到2.txt文件中。但是如果文件过大就会出现问题了,内存容易爆掉。那么这里比较合适的方式应该是读写交替进行,也就是使用流的方式读写文件,这样不管文件有多大,都不会一下子耗尽内存,可以安全的执行完。

如下:

let fs = require('fs');
let readStream = fs.createReadStream('./1.txt');
let writeStream = fs.createWriteStream('./2.txt');
readStream.on('data', function(chunk) { // 当有数据流出时,写入数据,chunk的类型为Buffer
    writeStream.write(chunk);
});
readStream.on('end', function() { // 当没有数据时,关闭数据流
    writeStream.end();
}); 

流的四种类型

在nodejs中,有四种stream类型:

  • Readable - 可读的流,用来读取数据 (例如 fs.createReadStream()).
  • Writable - 可写的流,用来写数据 (例如 fs.createWriteStream()).
  • Duplex - 可读写的流(双工流),可读+可写 (例如 net.Socket).
  • Transform - 转换流,在读写过程中可以修改和变换数据的 Duplex 流 (比如 zlib.createDeflate()(数据压缩/解压)).

1、可读流(Readable streams)

nodejs中常见的可读流有:fs.createReadStream()、http.IncomingRequest、process.stdin

可读流createReadStream用法如下:
// 创建可读流
let rs = fs.createReadStream(path,[options]);

// 设置编码格式
rs.setEncoding('utf8');

// 监听open事件,打开文件时触发
rs.on('open', function () {
    console.log(err);
});

//流切换到流动模式,数据会被尽可能快的读出
rs.on("data",function(data){
    console.log(data); //读取到的数据
});

// 该事件会在读完数据后被触发
rs.on("end",function(data){
    console.log("数据已经读取完毕");
});

//如果读取文件出错了,会触发error事件
rs.on("error",function(err){
    console.log("something is wrong during processing");
})

//文件关闭触发
rs.on('close', function () {
     console.log('文件关闭');
});

1、path读取文件的路径
2、options

  • flags打开文件要做的操作,默认为'r'
  • encoding默认为null
  • start开始读取的索引位置
  • end结束读取的索引位置(包括结束位置)
  • highWaterMark读取缓存区默认的大小64kb
    如果指定utf8编码highWaterMark要大于3个字节

2、可写流(Writable streams)

可写流createReadStream

实现了stream.Readable接口的对象,将对象数据读取为流数据,当监听data事件后,开始发射数据

 let  fs = require("fs");
// 创建一个可以写入的流,写入到文件 1.txt 中
let  ws= fs.createWriteStream('1.txt');
let  data = '写入流数据';
 
// 使用 utf8 编码写入数据
ws.write(data,'UTF8');
 
// 表明接下来没有数据要被写入 Writable 通过传入可选的 chunk 和 encoding 参数,可以在关闭流之前再写入一段数据 如果传入了可选的 callback 函数,它将作为 'finish' 事件的回调函数
ws.end("最后写入的数据","utf8",function(){
   console.log(" 我是'finish' 事件的回调函数")
});
 
// 在调用了 stream.end() 方法,且缓冲区数据都已经传给底层系统之后, 'finish' 事件将被触发。
ws.on('finish', function() {
  console.log("写入完成。");
});

// 写入时发生错误触发
ws.on('error', function(err){
  console.log(err.stack);
});
 
// 创建可写流
let  ws = fs.createWriteStream(path,[options]);

1、path读取文件的路径
2、options

  • flags打开文件要做的操作,默认为'w'
  • encoding默认为utf8
  • highWaterMark写入缓存区的默认大小16kb

管道流pipe用法
将数据的滞留量限制到一个可接受的水平,以使得不同速度的来源和目标不会淹没可用内存。
linux经典的管道的概念,前者的输出是后者的输入
pipe是一种最简单直接的方法连接两个stream,内部实现了数据传递的整个过程,在开发的时候不需要关注内部数据的流动

用法:

var from = fs.createReadStream('./1.txt');
var to = fs.createWriteStream('./2.txt');
from.pipe(to); // 就是从1.txt中读一点就往2.txt中写一点

3、双工流(Duplex streams)

Duplex实际上就是继承了Readable和Writable。
有了双工流,我们可以在同一个对象上同时实现可读和可写,就好像同时继承这两个接口。 重要的是双工流的可读性和可写性操作完全独立于彼此。这仅仅是将两个特性组合成一个对象

const {Duplex} = require('stream');
const inoutStream = new Duplex({
    write(chunk, encoding, callback) {
        console.log(chunk.toString());
        callback();
    },
    read(size) {
        this.push((++this.index)+'');
        if (this.index > 3) {
            this.push(null);
        }
    }
});

inoutStream.index = 0;
process.stdin.pipe(inoutStream).pipe(process.stdout);

最常见的Duplex stream应该就是net.Socket实例了。

4、转换流(Transform streams)

转换流的输出是从输入中计算出来的,Transform stream是Duplex stream的特例。也就是说,Transform stream也同时可读可写,它可以用来修改或转换数据。然它跟Duplex stream的区别在于,Transform stream的输出与输入是存在相关性的。你可以认为转换流就是一个函数,这个函数的输入是一个可写流,输出是一个可读流。

对于转换流,我们不必实现read或write的方法,我们只需要实现一个transform方法,将两者结合起来。它有write方法的意思,我们也可以用它来push数据。

例如:希望将输入的内容转化成大写在输出出来

const {Transform} = require('stream');

const upperCase = new Transform({
    transform(chunk, encoding, callback) {
        this.push(chunk.toString().toUpperCase()); // 将输入的内容放入到可读流中
        callback();
    }
});
// 希望将输入的内容转化成大写在输出出来
process.stdin.pipe(upperCase).pipe(process.stdout);

常见的Transform stream包括zlib、crypto,这里有个简单例子:文件的gzip压缩。

let fs = require('fs');
let zlib = require('zlib');

let gzip = zlib.createGzip();

// 将1.txt文件的内容,打包压缩成compress.txt.gz
let inFile = fs.createReadStream('./file/1.txt');
let outGz = fs.createWriteStream('./file/compress.txt.gz');

inFile .pipe(gzip).pipe(outGz);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • stream 流是一个抽象接口,在 Node 里被不同的对象实现。例如 request to an HTTP se...
    明明三省阅读 3,392评论 1 10
  • 流的基本概念及理解 流是一种数据传输手段,是有顺序的,有起点和终点,比如你要把数据从一个地方传到另外一个地方流非常...
    October_yang阅读 7,665评论 3 9
  • 一、什么是Stream(流) 流(stream)在 Node.js 中是处理流数据的抽象接口(abstract i...
    Brolly阅读 5,374评论 0 0
  • 流是Node中最重要的组件和模式之一。在社区里有一句格言说:让一切事务流动起来。这已经足够来描述在Node中流...
    宫若石阅读 539评论 0 0
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139