当前位置: 首页 > news >正文

node.js学习之流解析(一)

在 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.txt
var 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

相关文章:

  • SpringBoot之@EnableAutoConfiguration原理及自定义扩展
  • CentOS 6.5 安全加固
  • python之路——常用模块
  • 排序算法之选择排序
  • PostgreSQL入门及提权
  • 面向对象1
  • Lambda表达式(Java)
  • 区块链将会怎样颠覆Google、Amazon、Facebook和Apple?
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • Windows Server 2012的服务管理自动化 -启动类型设置,手动启动还是自动启动
  • JVM 组成以及各部分作用
  • PHP 500报错的快速解决方法
  • windows网络模型之完成端口(CompletionPort)详解 (转)
  • [转]区块链代码快速学习实践
  • 《王牌特工2》情景再现,Youbionic推出可穿戴式机械手
  • C++类的相互关联
  • css选择器
  • es6
  • Golang-长连接-状态推送
  • Just for fun——迅速写完快速排序
  • JWT究竟是什么呢?
  • Octave 入门
  • Vue 重置组件到初始状态
  • vuex 学习笔记 01
  • 编写高质量JavaScript代码之并发
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 分布式任务队列Celery
  • 前言-如何学习区块链
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 深入浅出Node.js
  • 微信小程序上拉加载:onReachBottom详解+设置触发距离
  • 我与Jetbrains的这些年
  •  一套莫尔斯电报听写、翻译系统
  • 用简单代码看卷积组块发展
  • mysql面试题分组并合并列
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • 仓管云——企业云erp功能有哪些?
  • #1015 : KMP算法
  • #includecmath
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (HAL库版)freeRTOS移植STMF103
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (附源码)计算机毕业设计高校学生选课系统
  • (学习日记)2024.01.19
  • (一)WLAN定义和基本架构转
  • (一)认识微服务
  • .Net CoreRabbitMQ消息存储可靠机制
  • .NET的微型Web框架 Nancy
  • .NET企业级应用架构设计系列之结尾篇
  • .NET中 MVC 工厂模式浅析