阅读目录

一:nodeJS中的stream(流)的概念及作用?

什么是流呢?日常生活中有水流,我们很容易想得到的就是水龙头,那么水龙头流出的水是有序且有方向的(从高处往低处流)。我们在nodejs中的流也是一样的,他们也是有序且有方向的。nodejs中的流是可读的、或可写的、或可读可写的。
并且流继承了EventEmitter。因此所有的流都是EventEmitter的实列。

Node.js中有四种基本的流类型,如下:

1. Readable--可读的流(比如 fs.createReadStream()).
2. Writable--可写的流(比如 fs.createWriteStream()).
3. Duplex--可读写的流
4. Transform---在读写过程中可以修改和变换数据的Duplex流。

nodeJS中的流最大的作用是:读取大文件的过程中,不会一次性的读入到内存中。每次只会读取数据源的一个数据块。
然后后续过程中可以立即处理该数据块(数据处理完成后会进入垃圾回收机制)。而不用等待所有的数据。

我们先来看一个简单的流的实列来理解下:

1. 首先我们来创建一个大文件,如下代码:

const fs = require('fs');
const file = fs.createWriteStream('./big.txt');
// 循环500万次
for (let i = 0; i <= 5000000; i++) {
file.write('我是空智,我来测试一个大文件, 你看看我会有多大?');
} file.end();

我在我项目文件里面新建一个app.js文件,然后把上面的代码放入到 app.js 里面去,可以看到循环了500万次后,写入500万次数据到 big.txt中去,因此会在文件目录下生成一个 big.txt文件,如下:

该文件在我磁盘中显示345兆。

readFile读取该文件:

下面我们使用 readFile 来读取该文件看看(readFile会一次性读入到内存中)。

我们把app.js代码改成如下:

const fs = require('fs');
const Koa = require('koa'); const app = new Koa(); app.use(async(ctx, next) => {
const res = ctx.res;
fs.readFile('./big.txt', (err, data) => {
if (err) {
throw err;
} else {
res.end(data);
}
})
}); app.listen(3001, () => {
console.log('listening on 3001');
});

当我们运行node app.js 后,我们查看下该代码占用的内存(12MB)如下:

但是当我们运行 http://localhost:3001/ 后,发现占用的内存(有338MB了)如下:

readFile 它会把 big.txt的文件内容整个的读进以Buffer格式存入到内存中,然后再写进返回对象,那么这样的效率非常低的,并且如果该文件如果是1G或2G以上的文件,那么内存会直接被卡死掉的。或者服务器直接会奔溃掉。

下面我们使用 Node中的createReadStream方法就可以避免占用内存多的情况发生。我们把app.js 代码改成如下所示:

const fs = require('fs');
const Koa = require('koa'); const app = new Koa(); app.use(async(ctx, next) => {
const res = ctx.res;
const file = fs.createReadStream('./big.txt');
file.pipe(res);
}); app.listen(3001, () => {
console.log('listening on 3001');
});

然后我们继续查看内存的使用情况,如下所示:

可以看到我们的占用的内存只有12.8兆。也就是说:createReadStream 在读取大文件的过程中,不会一次性的读入到内存中。
每次只会读取数据源的一个数据块。这就是流的优点。下面我们来分别看下流吧。

二:fs.createReadStream() 可读流

其基本使用方法如下:

const fs = require('fs');
const rs = fs.createReadStream('./big.txt', {
flags: 'r', // 文件的操作方式,同readFile中的配置一样,这里默认是可读的是 r
encoding: 'utf-8', // 编码格式
autoClose: true, // 是否关闭读取文件操作系统内部使用的文件描述符
start: 0, // 开始读取的位置
end: 5, // 结束读取的位置
highWaterMark: 1 // 每次读取的个数
});

fs.createReadStream有以下监听事件:
具体有哪些事件可以查看官网(http://nodejs.cn/api/stream.html#stream_class_stream_readable) 这边先截图出来简单看看,如下所示:

有了上面这些监听方法,我们可以先看一个完整的实列,如下代码:

const fs = require('fs');
const file = fs.createReadStream('./msg.txt', {
flags: 'r', // 文件的操作方式,同readFile中的配置一样,这里默认是可读的是 r
encoding: 'utf-8', // 编码格式
autoClose: true, // 是否关闭读取文件操作系统内部使用的文件描述符
start: 0, // 开始读取的位置
end: 5, // 结束读取的位置
highWaterMark: 1 // 每次读取的个数
}); file.on('open', () => {
console.log('开始读取文件');
}); file.on('data', (data) => {
console.log('读取到的数据:');
console.log(data);
}); file.on('end', () => {
console.log('文件全部读取完毕');
}); file.on('close', () => {
console.log('文件被关闭');
}); file.on('error', (err) => {
console.log('读取文件失败');
});

执行如下图所示:

从上图我们可以看到,先打开文件,执行open事件,然后就是不断的触发data事件,等data事情读取结束后会触发end事件,然后会将文件关闭,触发close事件。

注意:msg.txt文件内容如下:hello world; 但是上面为什么只读了 hello了,那是因为我们上面限制了从开始读取位置读取,然后到结束位置结束(5). 并且限定了 highWaterMark: 1,每次读取的个数为1。当然如果我们改成每次读取的个数为2的话,那么每次会读2个字符。

pause() 方法:

如果我们在读取的过程中,想暂停事件的读取,我们可以使用 ReadStream对象的pause方法暂停data事件的触发。 如下代码:

file.on('data', (data) => {
console.log('读取到的数据:');
console.log(data);
file.pause();
});

然后如下图所示:

上面暂停了使用 pause()方法,如果我们现在想重新读取,需要使用 resume()方法,如下所示:

setTimeout(() => {
file.resume();
}, 100);

执行结果如下:

其他的一些事件,比如 readable事件等,可以看官方文档 (http://nodejs.cn/api/stream.html#stream_event_readable). 这里就不多分析了。

三:fs.createWriteStream() 可写流

如下代码演示:

const fs = require('fs');
const file = fs.createWriteStream('./1.txt', {
flags: 'w', // 文件的操作方式,同writeFile中的配置一样,这里默认是可读的是 w
encoding: 'utf-8', // 编码格式
autoClose: true, // 是否关闭读取文件操作系统内部使用的文件描述符
start: 0, // 开始读取的位置
highWaterMark: 1 // 每次写入的个数
}); let f1 = file.write('1', 'utf-8', () => {
console.log('写入成功1111');
}); f1 = file.write('2', 'utf-8', () => {
console.log('写入成功2222');
}); f1 = file.write('3', 'utf-8', () => {
console.log('写入成功3333');
}); // 标记文件末尾
file.end(); // 处理事件
file.on('finish', () => {
console.log('写入完成');
}); file.on('error', (err) => {
console.log(err);
});

在我项目的根目录下会生成一个 1.txt文件,里面有123内容。

详细请看官网(http://nodejs.cn/api/fs.html#fs_fs_writefile_file_data_options_callback

管道流(pipe)

我们需要把我们上面可读流读到的数据需要放到可写流中去写入到文件里面去。我们可以如下操作代码:

const fs = require('fs');

// 读取msg.txt中的字符串 hello world
const msg = fs.createReadStream('./msg.txt', {
highWaterMark: 5
}); // 写入到1.txt中
const f1 = fs.createWriteStream('./1.txt', {
encoding: 'utf-8',
highWaterMark: 1
}); // 监听读取的数据过程,把读取的数据写入到我们的1.txt文件里面去
msg.on('data', (chunk) => {
f1.write(chunk, 'utf-8', () => {
console.log('写入成功');
});
});

但是实现如上的机制,我们可以使用管道机制,管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。如下图所示:

如上代码,我们可以改成如下代码:

const fs = require('fs');

// 读取msg.txt中的字符串 hello world
const msg = fs.createReadStream('./msg.txt', {
highWaterMark: 5
}); // 写入到1.txt中
const f1 = fs.createWriteStream('./1.txt', {
encoding: 'utf-8',
highWaterMark: 1
}); const res = msg.pipe(f1);
console.log(res);

如上打印 res后,我们在命令行中查看下基本信息如下:

理解nodejs中的stream(流)的更多相关文章

  1. 【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?

    写在前面 先说点题外话:不少读者工作几年后,仍然在使用Java7之前版本的方法,对于Java8版本的新特性,甚至是Java7的新特性几乎没有接触过.真心想对这些读者说:你真的需要了解下Java8甚至以 ...

  2. [NodeJs系列][译]理解NodeJs中的Event Loop、Timers以及process.nextTick()

    译者注: 为什么要翻译?其实在翻译这篇文章前,笔者有Google了一下中文翻译,看的不是很明白,所以才有自己翻译的打算,当然能力有限,文中或有错漏,欢迎指正. 文末会有几个小问题,大家不妨一起思考一下 ...

  3. java中的Stream流

    java中的Stream流 说到Stream便容易想到I/O Stream,而实际上,谁规定"流"就一定是"IO流"呢?在Java 8中,得益于Lambda所带 ...

  4. Java8中的Stream流式操作 - 入门篇

    作者:汤圆 个人博客:javalover.cc 前言 之前总是朋友朋友的叫,感觉有套近乎的嫌疑,所以后面还是给大家改个称呼吧 因为大家是来看东西的,所以暂且叫做官人吧(灵感来自于民间流传的四大名著之一 ...

  5. Node 中的 stream (流)

    流的概念 流(stream)在 Node.js 中是处理流数据的抽象接口(abstract interface). stream 模块提供了基础的 API .使用这些 API 可以很容易地来构建实现流 ...

  6. 深入理解nodejs中的异步编程

    目录 简介 同步异步和阻塞非阻塞 javascript中的回调 回调函数的错误处理 回调地狱 ES6中的Promise 什么是Promise Promise的特点 Promise的优点 Promise ...

  7. 双层for循环用java中的stream流来实现

    //双重for循环for (int i = 0; i < fusRecomConfigDOList.size(); i++) { for (int j = 0; j < fusRecomC ...

  8. 理解 nodeJS 中的 buffer,stream

    在Node.js开发中,当遇到 buffer,stream,和二进制数据处理时,你是否像我一样,总是感到困惑?这种感觉是否会让你认为不了解它们,以为它们不适合你,认为而这些是Node.js作者们的事情 ...

  9. 77.深入理解nodejs中Express的中间件

    转自:https://blog.csdn.net/huang100qi/article/details/80220012 Express是一个基于Node.js平台的web应用开发框架,在Node.j ...

随机推荐

  1. 使用 cxf的程序 在win10 测试部署时报空指针异常

    2018-11-08 15:50:55.072 DEBUG 21524 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter  : ...

  2. 使用Python脚本伪造指定时间区间的数据库备份

    为监管需求,需要保留时间非常长的数据库备份.存储代价太大.所以存在了,临时抱佛脚,伪造备份.. 以下脚本功能,在于根据一个备份,复制出一段时间的备份.并且更改备份的文件时间戳.可以用shell轻松写出 ...

  3. c++学习书籍推荐《深入理解C++11 C++11新特性解析与应用》下载

    百度云及其他网盘下载地址:点我 编辑推荐 <深入理解C++11:C++11新特性解析与应用>编辑推荐:C++标准委员会成员和IBM XL编译器中国开发团队共同撰写,权威性毋庸置疑.系统.深 ...

  4. C语言学习书籍推荐《C语言程序设计 现代方法(第2版)》下载

    下载地址:点我 C语言仍然是计算机领域的通用语言之一,但现在的C语言已经和当初的时候大不相同了.本书主要的一个目的就是通过一种“现代方法”来介绍C语言,书中强调标准C,强调软件工程,不再强调“手工优化 ...

  5. WebRTC:数据传输相关协议简介

    对网络协议来说,需要做的通常就两件事情:1.建立连接,2.传输数据,WebRTC也不例外. 假设WebRTC应用的两端已经建立了连接,那么,剩下就是如何传输数据的问题了. WebRTC同时支持传输音视 ...

  6. Noip 2016 天天爱跑步 题解

    [NOIP2016]天天爱跑步 时间限制:2 s   内存限制:512 MB [题目描述] 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...

  7. OnCommandStateChange 不响应

    原因是我把原先的OnCommandStateChange( long nCommand, BOOL bEnable )大BOOL改成了小bool,回调不认识了.

  8. 入职两个月,WPF开发感想

    1 .新工作,新开始 2.WPF初次接触以及学习MVVM开发模式 3.后台数据操作,ORACLE 存储过程(边做边学) 4.总结 4.1工作开发中的小问题 ,遇到的坑:  4.2 解决的问题,学校到的 ...

  9. 个人永久性免费-Excel催化剂功能第86波-人工智能之图像OCR文本识别全覆盖

    在上一年中,Excel催化剂已经送上一波人工智能系列功能,鉴于部分高端用户的需求,再次给予实现了复杂的图像OCR识别,包含几乎所有日常场景,让公司个人手头的图像非结构化数据瞬间变为可进行结构化处理分析 ...

  10. 推荐 2 款超牛逼、炫酷、实用的Docker管理工具!

    Docker技术的火热程度,想必每个互联网IT技术人员都能时时感受的到,的确,近些年,国内对于Docker容器技术的应用需求越来越强烈!! 人均年薪80万以上,docker到底是什么?为什么这么火? ...