博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
node.js学习之流解析(一)
阅读量:7078 次
发布时间:2019-06-28

本文共 3138 字,大约阅读时间需要 10 分钟。

在 Node.js 中,读取文件的方式有两种,一种是用 fs.readFile,另外一种是利用流的方式,即 fs.createReadStream 方法来读取。

fs.readFile 对于每个 Node.js 使用者来说最熟悉不过了,简单易懂,很好上手。但是这个方法有很大的缺陷,它的缺点就是读取文件时候会先将数据全部读入内存,一旦遇到大文件的时候,这种方式读取的效率就非常低下了。因为假如全部读入内存,那么内存就会撑爆。

使用readFile或者readFileSync方法读取文件内容时,Nodejs首先将文件内容完整地读入缓存区,再从缓存区读取内容。

使用writeFile方法或writeFileSync方法写入文件内容时,Nodejs首先将文件内容完整地读入缓存区,然后一次性将缓存区内容写入文件中

也就是说readFile方法或readFileSync方法读取文件内容或者使用writeFIle或writeFileSync方法写入文件内容时,nodejs将该文件视为一个整体,为其分配缓存区并且一次性将文件内容读取到缓存区中,在这个期间,nodejs将不能执行任何其他操作。

而 fs.createReadStream 则是通过 Stream 来读取数据,它会把文件(数据)分割成小块,然后触发一些特定的事件,我们可以监听这些事件,编写特定的处理函数。这种方式相对上面来说,并不好上手,但它效率非常高。

那么流到底是什么呢?

我们可以把流类比水流形容数据的流动。在文件I/O、网络I/O中数据的传输都可以称之为流,流是能统一描述所有常见输入输出类型的模型,是顺序读写字节序列的抽象表示。数据从A端流向B端与从B端流向A端是不一样的,因此,流是有方向的。A端输入数据到B端,对B就是输入流,得到的对象就是可读流;对A就是输出端、得到的对象是可写流。有的流即可以读又可以写,如TCP连接,Socket连接等,称为读写流(Duplex)。还有一种在读写过程中可以修改和变换数据的读写流称为Transform流。

在node中,这些流中的数据就是Buffer对象,可读、可写流会将数据存储到内部的缓存中,等待被消费;Duplex 和Transform则是都维护了两个相互独立的缓存用于读和写。在维持了合理高效的数据流的同时,也使得对于读和写可以独立进行而互不影响。

在node中,这四种流都是EventEmitter的实例,它们都有close、error事件,可读流具有监听数据到来的data事件等,可写流则具有监听数据已传给低层系统的finish事件等,Duplex和Transform都同时实现了Readable和Writable的事件和接口

值得一提的是writable的drain事件,这个事件表示缓存的数据被排空了。为什么有这个事件呢?起因是调用可写流的write和可读流的read都会有一个缓存区用来缓存写/读的数据,缓存区是有大小的,一旦写的内容超过这个大小,write方法就会返回false,表示写入停止,这时如果继续read完缓存区数据,缓存区被排空,就会触发drain事件,可以这样来防止缓存区爆仓:

var rs = fs.createReadStream(src);var ws = fs.createWriteStream(dst);rs.on('data', function (chunk) {    if (ws.write(chunk) === false) {        rs.pause();    }});rs.on('end', function () {    ws.end();});ws.on('drain', function () {    rs.resume();});复制代码

一些常见流分类:

可写流:HTTP requests, on the client、HTTP responses, on the server、fs write streams、zlib streams、crypto streams、TCP sockets、child process stdin、process.stdout, process.stderr

可读流:HTTP responses, on the client、HTTP requests, on the server、fs read streams、zlib streams、crypto streams、TCP sockets、child process stdout and stderr、process.stdin

可读可写流:TCP sockets、zlib streams、crypto streams

变换流:zlib streams、crypto streams

另外,提到流就不得不提到管道的概念,这个概念也非常形象:水流从一端到另一端流动需要管道作为通道或媒介。流也是这样,数据在端之间的传送也需要管道,在node中是这样的:

// 将 readable 中的所有数据通过管道传递给名为 file.txt 的文件const readable = getReadableStreamSomehow();const writable = getWritableStreamSomehow('file.txt');// readable 中的所有数据都传给了 'file.txt'readable.pipe(writable);// 对流进行链式地管道操作const r = fs.createReadStream('file.txt');const z = zlib.createGzip();const w = fs.createWriteStream('file.txt.gz');r.pipe(z).pipe(w);复制代码

注意,只有可读流才具有pipe能力,可写流作为目的地。

pipe不仅可以作为通道,还能很好的控制管道里的流,控制读和写的平衡,不让任一方过度操作。另外,pipe可以监听可读流的data、end事件,这样就可以构建快速的响应:

// 一个文件下载的例子,使用回调函数的话需要等到服务器读取完文件才能向浏览器发送数据var http = require('http') ;var fs = require('fs') ;var server = http.createServer(function (req, res) {    fs.readFile(__dirname + '/data.txt', function (err, data) {        res.end(data);    }) ;}) ;server.listen(8888) ;// 而采用流的方式,只要建立连接,就会接受到数据,不用等到服务器缓存完data.txtvar http = require('http') var fs = require('fs') var server = http.createServer(function (req, res) {    var stream = fs.createReadStream(__dirname + '/data.txt')     stream.pipe(res) }) server.listen(8888) 复制代码

因此,使用pipe即可解决上面那个爆仓问题。

转载于:https://juejin.im/post/5acada986fb9a028d043bac0

你可能感兴趣的文章
express不是内部或外部命令
查看>>
通过反射获取成员方法并使用
查看>>
String StringBuffer StringBuilder
查看>>
bash的工作特性及命令状态返回查询
查看>>
Samba服务共享(匿名用户访问、本地用户访问、虚拟用户访问)
查看>>
HttpServletResponse输出乱码的问题
查看>>
你真的很熟分布式和事务吗?
查看>>
基于ThreadPoolExecutor实现工作引擎参考
查看>>
Go语言的基本数据类型
查看>>
WEB测试:***apache
查看>>
42 个移动端启动页面优化 Tips
查看>>
Egret之ProtoBuf安装
查看>>
Cocos2d-x3.0游戏实例《别救我》目录
查看>>
我的友情链接
查看>>
ASP开发中数据库文件调用的捷径
查看>>
debian启动项与服务设置
查看>>
WinPcap编程环境设置
查看>>
基于openssl的https服务配置
查看>>
从 CentOS 5.5 中精简出属于自己的专属Linux (三)
查看>>
C和指针---第十五章:输入/输出函数
查看>>