热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Node.js中StreamAPI的使用

基本介绍在Node.js中,读取文件的方式有两种,一种是用fs.readFile,另外一种是利用fs.createReadStream来读

基本介绍

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

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

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

事实上, Stream 在 Node.js 中并非仅仅用在文件处理上,其他地方也可以看到它的身影,如 process.stdin/stdout, http, tcp sockets, zlib, crypto 等都有用到。

本文是我学习 Node.js 中的 Stream API 中的一点总结,希望对大家有用。

特点

  • 基于事件通讯

  • 可以通过 pipe 来连接流

种类

  • Readable Stream 可读数据流

  • Writeable Stream 可写数据流

  • Duplex Stream 双向数据流,可以同时读和写

  • Transform Stream 转换数据流,可读可写,同时可以转换(处理)数据

事件

可读数据流的事件

  • readable 数据向外流时触发

  • data 对于那些没有显式暂停的数据流,添加data事件监听函数,会将数据流切换到流动态,尽快向外提供数据

  • end 读取完数据时触发。注意不能和 writeableStream.end() 混淆,writeableStream 并没有 end 事件,只有 .end() 方法

  • close 数据源关闭时触发

  • error 读取数据发生错误时触发

可写数据流的事件

  • drain writable.write(chunk) 返回 false 之后,缓存全部写入完成,可以重新写入时就会触发

  • finish 调用 .end 方法时,所有缓存的数据释放后触发,类似于可读数据流中的 end 事件,表示写入过程结束

  • pipe 作为 pipe 目标时触发

  • unpipe 作为 unpipe 目标时触发

  • error 写入数据发生错误时触发

状态

可读数据流有两种状态:流动态暂停态,改变数据流状态的方法如下:

暂停态 -> 流动态

  • 添加 data 事件的监听函数

  • 调用 resume 方法

  • 调用 pipe 方法

注意: 如果转为流动态时,没有 data 事件的监听函数,也没有 pipe 方法的目的地,那么数据将遗失。

流动态 -> 暂停态

  • 不存在 pipe 方法的目的地时,调用 pause 方法

  • 存在 pipe 方法的目的地时,移除所有 data 事件的监听函数,并且调用 unpipe 方法,移除所有 pipe 方法的目的地

注意: 只移除 data 事件的监听函数,并不会自动引发数据流进入「暂停态」。另外,存在 pipe 方法的目的地时,调用 pause 方法,并不能保证数据流总是处于暂停态,一旦那些目的地发出数据请求,数据流有可能会继续提供数据。

用法

读写文件

var fs = require('fs');
// 新建可读数据流
var rs = fs.createReadStream('./test1.txt');
// 新建可写数据流
var ws = fs.createWriteStream('./test2.txt');// 监听可读数据流结束事件
rs.on('end', function() {console.log('read text1.txt successfully!');
});
// 监听可写数据流结束事件
ws.on('finish', function() {console.log('write text2.txt successfully!');
});
// 把可读数据流转换成流动态,流进可写数据流中
rs.pipe(ws);

读取 CSV 文件,并上传数据(我在生产环境中写过)

var fs = require('fs');
var es = require('event-stream');
var csv = require('csv');
var parser = csv.parse();
var transformer = csv.transform(function(record) {return record.join(',');
});var data = fs.createReadStream('./demo.csv');
data.pipe(parser).pipe(transformer)// 处理前一个 stream 传递过来的数据.pipe(es.map(function(data, callback) {upload(data, function(err) {callback(err);});}))// 相当于监听前一个 stream 的 end 事件.pipe(es.wait(function(err, body) {process.stdout.write('done!');}));

更多用法

可以参考一下 https://github.com/jeresig/node-stream-playground ,进去示例网站之后直接点 add stream 就能看到结果了。

常见坑

  • rs.pipe(ws) 的方式来写文件并不是把 rs 的内容 append 到 ws 后面,而是直接用 rs 的内容覆盖 ws 原有的内容

  • 已结束/关闭的流不能重复使用,必须重新创建数据流

  • pipe 方法返回的是目标数据流,如 a.pipe(b) 返回的是 b,因此监听事件的时候请注意你监听的对象是否正确

    • 如果你要监听多个数据流,同时你又使用了 pipe 方法来串联数据流的话,你就要写成:

      data.on('end', function() {console.log('data end');}).pipe(a).on('end', function() {console.log('a end');}).pipe(b).on('end', function() {console.log('b end');});

常用类库

  • event-stream 用起来有函数式编程的感觉,个人比较喜欢

  • awesome-nodejs#streams 由于其他 stream 库我都没用过,所以有需求的就直接看这里吧

出处

https://scarletsky.github.io/2015/10/22/node-stream-api-learning/

参考资料

阮一峰 - stream接口
nodejs.org Stream
Transforming data with Node.js transform streams
NodeJS: What's the difference between a Duplex stream and a Transform stream?



推荐阅读
  • 本文介绍了如何在 Node.js 中使用 `setDefaultEncoding` 方法为可写流设置默认编码,并提供了详细的语法说明和示例代码。 ... [详细]
  • 本文介绍如何在Node.js环境中执行Powershell脚本,并详细说明了通过子进程处理命令输出和错误信息的具体步骤。 ... [详细]
  • 本文探讨了如何在Node.js环境中,通过Tor网络使用的SOCKS5代理执行HTTP请求。文中不仅提供了基础的实现方法,还介绍了几种常用的库和工具,帮助开发者解决遇到的问题。 ... [详细]
  • 本文详细介绍了 Node.js 中 Worker.isMainThread 属性的功能、用法及其实例代码,帮助开发者更好地理解和利用多线程技术。 ... [详细]
  • 本文深入探讨了UNIX/Linux系统中的进程间通信(IPC)机制,包括消息传递、同步和共享内存等。详细介绍了管道(Pipe)、有名管道(FIFO)、Posix和System V消息队列、互斥锁与条件变量、读写锁、信号量以及共享内存的使用方法和应用场景。 ... [详细]
  • Node.js 入门指南(一)
    本文介绍了Node.js的安装步骤、如何创建第一个应用程序、NPM的基本使用以及处理回调函数的方法。通过实际操作示例,帮助初学者快速掌握Node.js的基础知识。 ... [详细]
  • Node.js 中 GET 和 POST 请求的数据处理
    本文详细介绍了如何在 Node.js 中使用 GET 和 POST 方法来处理客户端发送的数据。通过示例代码展示了如何解析 URL 参数和表单数据,并提供了完整的实现步骤。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • 深入解析 Spring Security 用户认证机制
    本文将详细介绍 Spring Security 中用户登录认证的核心流程,重点分析 AbstractAuthenticationProcessingFilter 和 AuthenticationManager 的工作原理。通过理解这些组件的实现,读者可以更好地掌握 Spring Security 的认证机制。 ... [详细]
  • 配置Windows操作系统以确保DAW(数字音频工作站)硬件和软件的高效运行可能是一个复杂且令人沮丧的过程。本文提供了一系列专业建议,帮助你优化Windows系统,确保录音和音频处理的流畅性。 ... [详细]
  • 尽管深度学习带来了广泛的应用前景,其训练通常需要强大的计算资源。然而,并非所有开发者都能负担得起高性能服务器或专用硬件。本文探讨了如何在有限的硬件条件下(如ARM CPU)高效运行深度神经网络,特别是通过选择合适的工具和框架来加速模型推理。 ... [详细]
  • 解决VSCode中文乱码问题的综合方案
    在使用VSCode进行开发时,尤其是涉及Python编程,可能会遇到中文乱码的问题。本文总结了多种有效的解决方案,帮助开发者快速解决这一常见问题。 ... [详细]
  • 本文探讨了在Git子模块目录中运行pre-commit时遇到的错误,并提供了一种通过Docker环境解决此问题的方法。 ... [详细]
  • Alluxio 1.5.0 版本发布:增强功能与优化
    Alluxio 1.5.0 开源版本引入了多项新特性和改进,旨在提升数据访问速度和系统互操作性。 ... [详细]
author-avatar
夏y儿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有