Node.js中如何实现可读、可写、双工和转换流,构成Stream流?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1914个文字,预计阅读时间需要8分钟。
本章节带大家初步了解Node中的Stream,介绍如何引入Stream,实现可读流、可写流、双工流和转换流的操作方法,希望对大家有所帮助!
引入Stream:假设我们有这样一个需求,我们需要读取一个文件,并且实时地输出到控制台。我们可以使用Node中的Stream来实现这一需求。
javascriptconst fs=require('fs');const readline=require('readline');
const readStream=fs.createReadStream('example.txt');const rl=readline.createInterface({ input: readStream, crlfDelay: Infinity});
rl.on('line', (line)=> { console.log(line);});
rl.on('close', ()=> { console.log('文件读取完成!');});
以上代码创建了一个可读流,并通过readline模块将其转换为可读行流,然后逐行输出到控制台。
本篇文章带大家了解一下Node中的之Stream,介绍一下引入 Stream,实现可读流、可写流、双工流和转换流的方法,希望对大家有所帮助!
引入 Stream假设我们有这么一个需求,我们需要复制一个文件中的内容到另一个文件中,我们会写出以下代码
const fs = require('fs'); const path = require('path'); const copy = (source, target) => { fs.readFile(path.resolve(source), (err, data) => { if(err) { throw new Error(err.toString()); return; } fs.writeFile(path.resolve(target), data, (err) => { if(!err) { console.log("复制成功!"); } }) }) }
上面的代码很简单,就是先读取 source 文件里面的内容,然后将内容写入到 target 文件中。它的特点是需要读取完 source 里面的所有内容,然后将内容写入到 target 中。
这样做就有一个缺点,当我们读取大文件时,可能会发生内存不够用的情况,因为它会先将文件的所有内容都读取到内存;另外还就是时间,一次性读取一个大文件到内存,是需要比较长的时间的,用户可能会有卡顿的感觉。
另一种解决办法就是边读边写,读取部分文件内容,然后将内容写入到新文件中,这样在内存中的数据只是部分内容,不会占有太多的内存,由于是边读编写,用户可以很快的得到响应,提高用户体验。
在网上找到一幅动图来形象的展示使用流前后数据的流动情况
Node.js 给我们提供 Stream 的 API,它是专门用来处理大文件的。因为数据是一部分一部分的处理,就像是水流一样,所以这个模块的名称就称为 Stream。
const fs = require('fs'); function copy(source, target) { const rs = fs.createReadStream(source); const ws = fs.createWriteStream(target); rs.on('data', data => { ws.write(data); }); rs.on('end', () => { ws.end(); }); }
上面代码的细节将在后文揭晓。
Stream 的分类Stream 可以分为四类
- Readable:可读流,数据的提供者
- Writeable:可写流,数据的消费者
- Duplex:可写可读流(双工流)
- Transform:是 Duplex 的特殊情况,转换流,对输入的数据进行处理,然后输出
可读流与可写流是基础,常见的可读流与可写流如下
Stream 是 EventEmitter 的实例,有自定义的事件。
Readable Stream可读流有两个模式,暂停模式与流动模式。当我们创建一个流时,如果我们监听了 readable 事件,它就会来到暂停模式,在暂停模式下,它会不断的读取数据到缓冲区,当读取到的数据超过预设的大小时,它由属性 highWaterMark 指定(默认为 64kB),便会触发 readable 事件,readable 事件的触发有两种情况:
- 缓存区中的数据达到
highWaterMark预设的大小 - 数据源的数据已经被读取完毕
const fs = require('fs'); const rs = fs.createReadStream('a.txt', { highWaterMark: 1 // 缓存区最多存储 1 字节 }); rs.on('readable', () => { let data; while(data=rs.read()) { console.log(data.toString()); } })
上面的程序设置 highWaterMark 为 1,即每次读取到一个字节便会触发 readable 命令,每次当触发 readable 命令时,我们调用可读流的 read([size]) 方法从缓冲区中读取数据(读取到的数据为 Buffer),然后打印到控制台。
当我们为可读流绑定 data 事件时,可读流便会切换到流动状态,当位于流动状态时,可读流会自动的从文件中读取内容到缓冲区,当缓冲区中的内容大于设定的 highWaterMark 的大小时,便会触发 data 事件,将缓冲区中的数据传递给 data 事件绑定的函数。以上过程会自动不断进行。当文件中的所有内容都被读取完成时,那么就会触发 end 事件。
const fs = require('fs'); const rs = fs.createReadStream('a.txt', { highWaterMark: 2 }); rs.on('data', data => { console.log(data.toString()); }); rs.on('end', () => { console.log("文件读取完毕!"); });
暂停模式像是手动步枪,而流动模式则像是自动步枪。暂停模式与流动模式也可以相互切换,通过 pause() 可以从流动状态切换到暂停状态,通过 resume() 则可以从暂停模式切换到流动模式。
可读流的一个经典实例就是 我们还可以调用可写流的 const fs = require('fs');
const ws = fs.createWriteStream('b.txt');
ws.write('Hello');
ws.write('World');
ws.end('!');
ws.on('close', () => {
console.log("close"); // close
}) 当调用 const fs = require('fs');
const ws = fs.createWriteStream('b.txt');
ws.write('Hello');
ws.write('World');
ws.end('!');
ws.write('write again'); // Error [ERR_STREAM_WRITE_AFTER_END]: write after end 当调用 const fs = require('fs');
const ws = fs.createWriteStream('b.txt');
ws.write('Hello');
ws.write('World');
ws.end('!');
ws.on('close', () => {
console.log("close");
});
ws.on('finish', () => {
console.log("finish");
}); 打印结果是 finish
close 说明 可写流的经典例子就是 更多node相关知识,请访问:nodejs 教程!!www.w3.org/1998/Math/MathML" display="block">end() 方法,表示将缓存中的内容清空写入文件,并关闭文件,此时会触发 close 事件end() 方法之后就不能调用 write() 方法了,否则会报错end() 方法之后,并且数据缓冲区中的数据已经写入之后会触发可写流的 finish 事件finish 事件会在 close 事件之前被触发。nodejs.cn/api/stream.html)了。
本文共计1914个文字,预计阅读时间需要8分钟。
本章节带大家初步了解Node中的Stream,介绍如何引入Stream,实现可读流、可写流、双工流和转换流的操作方法,希望对大家有所帮助!
引入Stream:假设我们有这样一个需求,我们需要读取一个文件,并且实时地输出到控制台。我们可以使用Node中的Stream来实现这一需求。
javascriptconst fs=require('fs');const readline=require('readline');
const readStream=fs.createReadStream('example.txt');const rl=readline.createInterface({ input: readStream, crlfDelay: Infinity});
rl.on('line', (line)=> { console.log(line);});
rl.on('close', ()=> { console.log('文件读取完成!');});
以上代码创建了一个可读流,并通过readline模块将其转换为可读行流,然后逐行输出到控制台。
本篇文章带大家了解一下Node中的之Stream,介绍一下引入 Stream,实现可读流、可写流、双工流和转换流的方法,希望对大家有所帮助!
引入 Stream假设我们有这么一个需求,我们需要复制一个文件中的内容到另一个文件中,我们会写出以下代码
const fs = require('fs'); const path = require('path'); const copy = (source, target) => { fs.readFile(path.resolve(source), (err, data) => { if(err) { throw new Error(err.toString()); return; } fs.writeFile(path.resolve(target), data, (err) => { if(!err) { console.log("复制成功!"); } }) }) }
上面的代码很简单,就是先读取 source 文件里面的内容,然后将内容写入到 target 文件中。它的特点是需要读取完 source 里面的所有内容,然后将内容写入到 target 中。
这样做就有一个缺点,当我们读取大文件时,可能会发生内存不够用的情况,因为它会先将文件的所有内容都读取到内存;另外还就是时间,一次性读取一个大文件到内存,是需要比较长的时间的,用户可能会有卡顿的感觉。
另一种解决办法就是边读边写,读取部分文件内容,然后将内容写入到新文件中,这样在内存中的数据只是部分内容,不会占有太多的内存,由于是边读编写,用户可以很快的得到响应,提高用户体验。
在网上找到一幅动图来形象的展示使用流前后数据的流动情况
Node.js 给我们提供 Stream 的 API,它是专门用来处理大文件的。因为数据是一部分一部分的处理,就像是水流一样,所以这个模块的名称就称为 Stream。
const fs = require('fs'); function copy(source, target) { const rs = fs.createReadStream(source); const ws = fs.createWriteStream(target); rs.on('data', data => { ws.write(data); }); rs.on('end', () => { ws.end(); }); }
上面代码的细节将在后文揭晓。
Stream 的分类Stream 可以分为四类
- Readable:可读流,数据的提供者
- Writeable:可写流,数据的消费者
- Duplex:可写可读流(双工流)
- Transform:是 Duplex 的特殊情况,转换流,对输入的数据进行处理,然后输出
可读流与可写流是基础,常见的可读流与可写流如下
Stream 是 EventEmitter 的实例,有自定义的事件。
Readable Stream可读流有两个模式,暂停模式与流动模式。当我们创建一个流时,如果我们监听了 readable 事件,它就会来到暂停模式,在暂停模式下,它会不断的读取数据到缓冲区,当读取到的数据超过预设的大小时,它由属性 highWaterMark 指定(默认为 64kB),便会触发 readable 事件,readable 事件的触发有两种情况:
- 缓存区中的数据达到
highWaterMark预设的大小 - 数据源的数据已经被读取完毕
const fs = require('fs'); const rs = fs.createReadStream('a.txt', { highWaterMark: 1 // 缓存区最多存储 1 字节 }); rs.on('readable', () => { let data; while(data=rs.read()) { console.log(data.toString()); } })
上面的程序设置 highWaterMark 为 1,即每次读取到一个字节便会触发 readable 命令,每次当触发 readable 命令时,我们调用可读流的 read([size]) 方法从缓冲区中读取数据(读取到的数据为 Buffer),然后打印到控制台。
当我们为可读流绑定 data 事件时,可读流便会切换到流动状态,当位于流动状态时,可读流会自动的从文件中读取内容到缓冲区,当缓冲区中的内容大于设定的 highWaterMark 的大小时,便会触发 data 事件,将缓冲区中的数据传递给 data 事件绑定的函数。以上过程会自动不断进行。当文件中的所有内容都被读取完成时,那么就会触发 end 事件。
const fs = require('fs'); const rs = fs.createReadStream('a.txt', { highWaterMark: 2 }); rs.on('data', data => { console.log(data.toString()); }); rs.on('end', () => { console.log("文件读取完毕!"); });
暂停模式像是手动步枪,而流动模式则像是自动步枪。暂停模式与流动模式也可以相互切换,通过 pause() 可以从流动状态切换到暂停状态,通过 resume() 则可以从暂停模式切换到流动模式。
可读流的一个经典实例就是 我们还可以调用可写流的 const fs = require('fs');
const ws = fs.createWriteStream('b.txt');
ws.write('Hello');
ws.write('World');
ws.end('!');
ws.on('close', () => {
console.log("close"); // close
}) 当调用 const fs = require('fs');
const ws = fs.createWriteStream('b.txt');
ws.write('Hello');
ws.write('World');
ws.end('!');
ws.write('write again'); // Error [ERR_STREAM_WRITE_AFTER_END]: write after end 当调用 const fs = require('fs');
const ws = fs.createWriteStream('b.txt');
ws.write('Hello');
ws.write('World');
ws.end('!');
ws.on('close', () => {
console.log("close");
});
ws.on('finish', () => {
console.log("finish");
}); 打印结果是 finish
close 说明 可写流的经典例子就是 更多node相关知识,请访问:nodejs 教程!!www.w3.org/1998/Math/MathML" display="block">end() 方法,表示将缓存中的内容清空写入文件,并关闭文件,此时会触发 close 事件end() 方法之后就不能调用 write() 方法了,否则会报错end() 方法之后,并且数据缓冲区中的数据已经写入之后会触发可写流的 finish 事件finish 事件会在 close 事件之前被触发。nodejs.cn/api/stream.html)了。

