NodeJS —— Stream 的基本使用

WinDbg调试工具安装全攻略:从下载到配置的保姆级教程(Windows 10/11适用) 本文提供了一份详细的WinDbg调试工具安装与配置指南,适用于Windows 10/11用户。文章对比了WinDbg Preview独立版与SDK内置经典版的区别,并手把手指导从Microsoft Store或Windows SDK完成安装。核心部分重点讲解了如何配置符号文件路径与源代码路径,这是让WinDbg发挥强大调试功能的关键步骤,并针对常见安装问题提供了实用的排错方法。 阅读详情

在这里插入图片描述


阅读原文


流的介绍

在 NodeJS 中,我们对文件的操作需要依赖核心模块 fs,fs 中有很基本 API 可以帮助我们读写占用内存较小的文件,如果是大文件或内存不确定也可以通过 open、read、write、close 等方法对文件进行操作,但是这样操作文件每一个步骤都要关心,非常繁琐,fs 中提供了可读流和可写流,让我们通过流来操作文件,方便我们对文件的读取和写入。


可读流

1、createReadStream 创建可读流

createReadStream 方法有两个参数,第一个参数是读取文件的路径,第二个参数为 options 选项,其中有八个参数:

  • flags:标识位,默认为 r;
  • encoding:字符编码,默认为 null;
  • fd:文件描述符,默认为 null;
  • mode:权限位,默认为 0o666;
  • autoClose:是否自动关闭文件,默认为 true;
  • start:读取文件的起始位置;
  • end:读取文件的(包含)结束位置;
  • highWaterMark:最大读取文件的字节数,默认 64 * 1024。

createReadStream 的返回值为 fs.ReadStream 对象,读取文件的数据在不指定 encoding 时,默认为 Buffer。

const fs = require("fs");

// 创建可读流,读取 1.txt 文件
let rs = fs.creatReadStream("1.txt", {
    start: 0,
    end: 3,
    highWaterMark: 2
});

在创建可读流后默认是不会读取文件内容的,读取文件时,可读流有两种状态,暂停状态和流动状态。

注意:本篇的可写流为流动模式,流动模式中有暂停状态和流动状态,而不是暂停模式,暂停模式是另一种可读流 readable。

2、流动状态

流动状态的意思是,一旦开始读取文件,会按照 highWaterMark 的值一次一次读取,直到读完为止,就像一个打开的水龙头,水不断的流出,直到流干,需要通过监听 data 事件触发。

假如现在 1.txt 文件中的内容为 0~9 十个数字,我们现在创建可读流并用流动状态读取。

// 流动状态
const fs = require("fs");

let rs = fs.createReadStream("1.txt", {
    start: 0,
    end: 3,
    highWaterMark: 2
});

// 读取文件
rs.on("data", data => {
    console.log(data);
});

// 监听读取结束
rs.on("end", () => {
    console.log("读完了");
});

// <Buffer 30 31>
// <Buffer 32 33>
// 读完了

在上面代码中,返回的 rs 对象监听了两个事件:

  • data:每次读取 highWaterMark 个字节,触发一次 data 事件,直到读取完成,回调的参数为每次读取的 Buffer;
  • end:当读取完成时触发并执行回调函数。

我们希望最后读到的结果是完整的,所以我们需要把每一次读到的结果在 data 事件触发时进行拼接,以前我们可能使用下面这种方式。

// 错误拼接数据的方式
const fs = require("fs");

let rs = fs.createReadStream("1.txt", {
    start: 0,
    end: 3,
    highWaterMark: 2
});

let str = "";

rs.on("data", data => {
    str += data;
});

rs.on("end", () => {
    console.log(str);
});

// 0123

在上面代码中如果读取的文件内容是中文,每次读取的 highWaterMark 为两个字节,不能组成一个完整的汉字,在每次读取时进行 += 操作会默认调用 toString 方法,这样会导致最后读取的结果是乱码。

在以后通过流操作文件时,大部分情况下都是在操作 Buffer,所以应该用下面这种方式来获取最后读取到的结果。

// 正确拼接数据的方式
const fs = require("fs");

let rs = fs.createReadStream("1.txt", {
    start: 0,
    end: 3,
    highWaterMark: 2
});

// 存储每次读取回来的 Buffer
let bufArr = [];

rs.on("data", data => {
    bufArr.push(data);
});

rs.on("end", () => {
    console.log(Buffer.concat(bufArr).toString());
});

// 0123

3、暂停状态

在流动状态中,一旦开始读取文件,会不断的触发 data 事件,直到读完,暂停状态是我们每读取一次就直接暂停,不再继续读取,即不再触发 data 事件,除非我们主动控制继续读取,就像水龙头打开放水一次后马上关上水龙头,下次使用时再打开。

类似于开关水龙头的动作,也就是暂停和恢复读取的动作,在可读流返回的 rs 对象上有两个对应的方法,pause 和 resume。

在下面的场景中我们把创建可读流的结尾位置更改成 9,在每次读两个字节并暂停一秒后恢复读取,直到读完 0~9 十个数字。

// 暂停状态
const fs = require("fs");

let rs = fs.createReadStream("1.txt", {
    start: 0,
    end: 9,
    highWaterMark: 2
});

let bufArr = [];

rs.on("data", data => {
    bufArr.push(data);
    rs.pause(); // 暂停读取
    console.log("暂停", new Date());

    setTimeout(() => {
        rs.resume(); // 恢复读取
    }, 1000)
});

rs.on("end", () => {
    console.log(Buffer.concat(bufArr).toString());
});

// 暂停 2018-07-03T23:52:52.436Z
// 暂停 2018-07-03T23:52:53.439Z
// 暂停 2018-07-03T23:52:54.440Z
// 暂停 2018-07-03T23:52:55.442Z
// 暂停 2018-07-03T23:52:56.443Z
// 0123456789

4、错误监听

在通过可读流读取文件时都是异步读取,在异步读取中如果遇到错误也可以通过异步监听到,可读流返回值 rs 对象可以通过 error 事件来监听错误,在读取文件出错时触发回调函数,回调函数参数为 err,即错误对象。

// 错误监听
const fs = require("fs");

// 读取一个不存在的文件
let rs = fs.createReadStream("xxx.js", {
    highWarterMark: 2
});

let bufArr = [];

rs.on("data", data => {
    bufArr.push(data);
});

rs.on("err", err => {
    console.log(err);
});

rs.on("end", () => {
    console.log(Buffer.concat(bufArr).toString());
});

// { Error: ENOENT: no such file or directory, open '......xxx.js' ......}

5、打开和关闭文件的监听

流的适用性非常广,不只是文件读写,也可以用在 http 中数据的请求和响应上,但是在针对文件读取返回的 rs 上有两个专有的事件用来监听文件的打开与关闭。

open 事件用来监听文件的打开,回调函数在打开文件后执行,close 事件用来监听文件的关闭,如果创建的可读流的 autoClose 为 true,在自动关闭文件时触发,回调函数在关闭文件后执行。

// 打开和关闭可读流的监听
const fs = require("fs");

let rs = fs.createReadStream("1.txt", {
    start: 0,
    end: 3,
    highWaterMark: 2
});

rs.on("open", () => {
    console.log("open");
});

rs.on("close", () => {
    console.log("close");
});

// open

在上面代码我们看出只要创建了可读流就会打开文件触发 open 事件,因为默认为暂停状态,没有对文件进行读取,所以不会关闭文件,即不会触发 close 事件。

// 暂停状态
const fs = require("fs");

let rs = fs.createReadStream("1.txt", {
    start: 0,
    end: 3,
    highWaterMark: 2
});

rs.on("open", () => {
    console.log("open");
});

rs.on("data", data => {
    console.log(data);
});

rs.on("end", () => {
    console.log("end");
});

rs.on("close", () => {
    console.log("close");
});

// open
// <Buffer 30 31>
// <Buffer 32 33>
// end
// close

从上面例子执行的打印结果可以看出只有开始读取文件并读完后,才会关闭文件并触发 close 事件,end 事件的触发要早于 close。


可写流

1、createWriteStream 创建可写流

createWriteStream 方法有两个参数,第一个参数是读取文件的路径,第二个参数为 options 选项,其中有七个参数:

  • flags:标识位,默认为 w;
  • encoding:字符编码,默认为 utf8;
  • fd:文件描述符,默认为 null;
  • mode:权限位,默认为 0o666;
  • autoClose:是否自动关闭文件,默认为 true;
  • start:写入文件的起始位置;
  • highWaterMark:一个对比写入字节数的标识,默认 16 * 1024。

createWriteStream 返回值为 fs.WriteStream 对象,第一次写入时会真的写入文件中,继续写入,会写入到缓存中。

// 创建可写流
const fs = require("fs");

// 创建可写流,写入 2.txt 文件
let ws = fs.createWriteStream("2.txt", {
    start: 0,
    highWaterMark: 3
});

2、可写流的 write 方法

在可写流中将内容写入文件需要使用 ws 的 write 方法,参数为写入的内容,返回值是一个布尔值,代表 highWaterMark 的值是否足够当前的写入,如果足够,返回 true,否则返回 false,换种说法就是写入内容的长度是否超出了 highWaterMark,超出返回 false。

// write 方法写入
const fs = require("fs");

let ws = fs.createWriteSteam("2.txt", {
    start: 0,
    highWaterMark: 3
});

let flag1 = ws.write("1");
console.log(flag1);

let flag2 = ws.write("2");
console.log(flag2);

let flag3 = ws.write("3");
console.log(flag3);

// true
// true
// false

写入不存在的文件时会自动创建文件,如果 start 的值不是 0,在写入不存在的文件时默认找不到写入的位置。

3、可写流的 drain 事件

drain 意为 “吸干”,当前写入的内容已经大于等于了 highWaterMark,会触发 drain 事件,当内容全部从缓存写入文件后,会执行回调函数。

// drain 事件
const fs = require("fs");

let ws = fs.createWriteStream("2.txt", {
    start: 0,
    highWaterMark: 3
});

let flag1 = ws.write("1");
console.log(flag1);

let flag2 = ws.write("2");
console.log(flag2);

let flag3 = ws.write("3");
console.log(flag3);


ws.on("drain", () => {
    console.log("吸干");
});

// true
// true
// false

4、可写流的 end 方法

end 方法传入的参数为最后写入的内容,end 会将缓存未写入的内容清空写入文件,并关闭文件。

// end 方法
const fs = require("fs");

let ws = fs.createWriteStream("2.txt", {
    start: 0,
    highWaterMark: 3
});

let flag1 = ws.write("1");
console.log(flag1);

let flag2 = ws.write("2");
console.log(flag2);

let flag3 = ws.write("3");
console.log(flag3);

ws.on("drain", () => {
    console.log("吸干");
});

ws.end("写完了");

// true
// true
// false

在调用 end 方法后,即使再次写入的值超出了 highWaterMark 也不会再触发 drain 事件了,此时打开 2.txt 后发现文件中的内容为 “123写完了”。

// 常见报错
const fs = require("fs");

let ws = fs.createWriteStream("2.txt", {
    start: 0,
    highWaterMark: 3
});

ws.write("1");
ws.end("写完了");
ws.write("2");

// Error [ERR_STREAM_WRITE_AFTER_END]: write after end...

在调用 end 方法后,不可以再调用 write 方法写入,否则会报一个很常见的错误 write after end,文件原有内容会被清空,而且不会被写入新内容。


可写流与可读流混合使用

可写流和可读流一般配合来使用,读来的内容如果超出了可写流的 highWaterMark,则调用可读流的 pause 暂停读取,等待内存中的内容写入文件,未写入的内容小于 highWaterMark 时,调用可写流的 resume 恢复读取,创建可写流返回值的 rs 上的 pipe 方法是专门用来连接可读流和可写流的,可以将一个文件读来的内容通过流写到另一个文件中。

// pipe 方法使用
const fs = require("fs");

// 创建可读流和可写流
let rs = fs.createReadStream("1.txt", {
    highWaterMark: 3
});
let ws = fs.createWriteStream("2.txt", {
    highWaterMark: 2
});

// 将 1.txt 的内容通过流写入 2.txt 中
rs.pipe(ws);

通过上面的这种类似于管道的方式,将一个流从一个文件输送到了另一个文件中,而且会根据读流和写流的 highWaterMark 自由的控制写入的 “节奏”,不用担心内存的消耗。


总结

这篇是关于读流和写流的基本用法,在平时的开发当中,大多数的 API 都用不到,只有最后的 pipe 用的最多,无论是在文件的读写还是请求的响应,其他的 API 虽然用的少,但是作为一个合格的程序员一定要有所了解。


【保姆级教程】YOLOv8_Pose多目标+关键点检测:训练自己的数据集 阅读详情

相关推荐

Contest 226 - C - Martial artist

from atcoder Problem Statement Time Limit: 2 sec / Memory Limit: 1024 MB Score :300300points Takahashi is a martial artist. There areNNmoves that a martial artist can learn, called Move11,22,\ldots…,NN. For each1 \leq i \leq N1≤i≤N, it takes...

m0_54674275的博客 1020

node中的读取流createReadStream、写入流createWriteStream和管道流pipe

一、读取流createReadStream 现在在data目录下有input.txt文件,其内容很多,如下 const fs = require('fs') let readStream = fs.createReadStream('./data/input.txt'); let count = 0; let str = "" readStream.on('data', data => { str += data; count++ }) readStream.on(

1万+

从零搭建 AI 运维 Agent:告警自动诊断 + 修复的完整技术方案

适合读者:管理 5-50 台服务器的运维/SRE 工程师、技术负责人 技术栈:FastAPI + React + PostgreSQL + Redis + DeepSeek 关键词:AI运维、AIOps、自动化运维、智能告警、告警修复、Runbook自动化、DevOps、SRE、DeepSeek运维

weixin_38359457的博客 1640

浅析node中流应用(一) 可读流(fs.createReadStream)

为什么要需要流? 当我们学习新知识的时候,首先我们知道为什么要学习,那我们为什么要学习流?因为在在node中读取文件的方式有来两种,一个是利用fs模块,一个是利用流来读取。如果读取小文件,我们可以使用fs读取,fs读取文件的时候,是将文件一次性读取到本地内存。而如果读取一个大文件,一次性读取会占用大量内存,效率很低,这个时候需要用流来读取。流是将数据分割段,一段一段的读取,可以控制速率,效率很...

weixin_34212189的博客 1万+

无监督低照度增强实战:用ZeroDCE和SCI拯救你的夜间照片(附PyTorch代码)

本文深入探讨了无监督低照度图像增强技术,重点解析了ZeroDCE和SCI两大代表性模型的核心原理与实战应用。通过对比分析两者的技术路径、网络结构及性能特点,并附上详细的PyTorch代码实现,旨在帮助读者掌握无需配对数据即可有效提升夜间照片画质的深度学习解决方案。

git9versioner的博客 490

STM32F030F4P6 ADC多通道定时器触发DMA搬运

主函数中等待转换完成的标志位,然后打印出来。ADC回调函数中将数值赋值给变量。配置定时器1s读一次。

qq_24426625的博客 1356

createWriteStream

createWriteStream是Node.js中的一个方法,可以用来创建一个可写流。可写流是一种用于写入数据的流。它允许我们在不断写入数据的同时将数据写入文件或其他目标。可写流是流的一种,流是Node.js中的一种抽象概念,表示一个数据流的生成器。可写流可以用于大型数据集的处理,因为它们可以边生成数据边写入文件,而无需将整个数据集加载到内存中。 举个例子,假设你想要将一个大型文件的数据写入另一...

weixin_42584758的博客 1100

基于深度学习的茶叶等级检测识别系统:YOLOv10 + UI界面 + 数据集

YOLOv10是YOLO系列的最新版本,继承了YOLO模型的优点,并对速度和精度进行了优化。YOLOv10在目标检测中使用卷积神经网络(CNN)作为基础,通过改进网络结构和优化训练策略,实现了在更低的延迟下进行高精度的目标检测。本文详细介绍了基于YOLOv10的茶叶等级检测与识别系统的设计与实现过程。我们从数据集准备、模型训练与推理、UI界面设计等多个方面进行了详细讲解,并给出了相应的代码实现。通过本系统,我们可以高效地自动检测茶叶的等级,并通过UI界面直观地展示结果。

YOLO 765

NodeJS Stream(可读流、可写流) API解读

流的介绍在 NodeJS 中,我们对文件的操作需要依赖核心模块 fs,fs 中有很基本 API 可以帮助我们读写占用内存较小的文件,如果是大文件或内存不确定也可以通过 open、read、write、close 等方法对文件进行操作,但是这样操作文件每一个步骤都要关心,非常繁琐,fs 中提供了可读流和可写流,让我们通过流来操作文件,方便我们对文件的读取和写入。可读流1、createReadStre...

weixin_34321977的博客 1008

Ubuntu Rtx3090 Cuda11.7 安装 pytorch3d

pytorch ubuntu pytorch3d 安装

G果的博客 2273

26.Labview波形图、XY图、强度图使用精讲

1. 波形图基础概念,如何使用,1.1 波形图与波形图表1.2 XY图如何设计、如何使用?1.3 强度图原理、如何使用、使用案例;2. 图形的参数设置2.1 波形图及波形图表的外观设计2.2 XY图的前面板设计2.3 强度图属性设置。

关注我,更多技术问题欢迎私聊~ 7501

关于NodeJS的的Stream

关于NodeJS的的Stream

太阳与星辰的博客 1331

STM32堆栈溢出问题及解决方法

堆栈是用于存储函数调用和局部变量的内存区域,它是一种先进后出的数据结构。如果函数嵌套层级过深或者局部变量占用的内存空间过大,堆栈的大小可能不足以容纳这些信息,从而导致堆栈溢出。在一些特殊情况下,可以使用堆内存替代堆栈,以避免堆栈溢出问题。在STM32的工程中,可以通过修改链接脚本文件(.ld文件)中的堆栈大小参数来增加堆栈空间。通常,堆栈大小设置为RAM的一小部分,但需要根据具体应用的需求进行调整。堆栈溢出是嵌入式系统开发中常见的问题之一,特别是在使用STM32微控制器时。3.3 使用堆内存替代堆栈。

ZwcCsharp的博客 2593

NodeJS stream入门

本文简介 点赞 + 关注 + 收藏 = 学会了 Stream 是一个概念,翻译成中文就是 “流” 的意思。它并非 NodeJS 独有。 本文就用 NodeJS 举例说明 Stream 。 Stream 简介 在传输大文件、视频、音频时,通常是分段传输的,可以把这个概念粗略的理解成 Stream。 Stream 的中文意思是 “流” ,我在网上找了个图很好的讲明这个东西。 传多少过来就接多少,而这个传输过程我们使用 NodeJS 时是不需要关心的,因为 NodeJS 提供了对应的方法来处理。 在

weixin_39415598的博客 429

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Nodejs Stream pipe 的使用与实现原理分析

今日文章由 “Node.js技术栈@五月君” 授权分享,正文从下面开始~通过流我们可以将一大块数据拆分为一小部分一点一点的流动起来,而无需一次性全部读入,在 Linux 下我们可以通过 ...

程序员成长指北 2223

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

NodeJS Stream入门

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情 本文简介 点赞 + 关注 + 收藏 = 学会了 Stream 是一个概念,翻译成中文就是 “流” 的意思。它并非 NodeJS 独有。 本文就用 NodeJS 举例说明 Stream 。 Stream 简介 在传输大文件、视频、音频时,通常是分段传输的,可以把这个概念粗略的理解成 Stre...

weixin_39415598的博客 155

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

探索NodeJS中的Stream:数据流动的艺术

探索NodeJS中的Stream:数据流动的艺术 在编程的广阔天地里,Stream(流)无疑是一个令人着迷的概念。今天,我们将一起揭开NodeJS中Stream的神秘面纱,探索它如何优雅地处理大量数据,以及为何这一特性使之成为现代开发不可或缺的一部分。 一、项目简介 本项目深入浅出地讲解了NodeJS中的Stream概念,适合所有想要理解这一复杂但强大的特性的开发者。通过一系列精心设计的例子,作者...

gitblog_00682的博客 491

好程序员web前端分享Nodejs学习笔记之Stream模块

好程序员web前端分享Nodejs学习笔记之Stream模块  一,开篇分析  流是一个抽象接口,被 Node 中的很多对象所实现。比如对一个 HTTP 服务器的请求是一个流,stdout 也是一个流。流是可读,可写或兼具两者的。  最早接触Stream是从早期的unix开始的, 数十年的实践证明Stream 思想可以很简单的开发出一些庞大的系统。  在unix里,Stream是通过 "|" 实现...

qq_35970166的博客 1503

好程序员web前端分享Nodejs学习笔记之Stream模块 

好程序员 web前端分享 Nodejs学习笔记之Stream模块   一,开篇分析   流是一个抽象接口,被...

cique2620的博客 146

nodeJS之流stream

前面的话   当内存中无法一次装下需要处理的数据时,或者一边读取一边处理更加高效时,我们就需要用到数据流。NodeJS中通过各种Stream来提供对数据流的操作。本文将详细说明NodeJS中的流stream   概述   流(stream)在Nodejs中是处理流数据的抽象接口。stream模块提供了基础的API 。使用这些API可以很容易地来构建实现流接口的对象。Nodejs提供了多种流...

weixin_33896726的博客 442

js写入文件到本地_Nodejs 中基于 Stream 的多文件合并实现

本文先从一个 Stream 的基本示例开始,有个初步认识,中间会讲在 Stream 中什么时候会出现内存泄漏,及如何避免最后基于 Nodejs 中的 Stream 实现一个多文件合并为一个文件的例子。一个简单的 Stream 操作创建一个可读流 readable 一个可写流 writeable,通过管道 pipe 将可写流绑定到可读流,一个简单的 Stream 操作就完成了。const...

weixin_39794213的博客 262

Nodejs中fs中文件stream读写方式

weixin_44132305的博客 741

NodeJS stream.pipeline 读写文件那些事

Node 最简单的 stream 用法就像下边这样: const fs = require('fs') const zlib = require('zlib') fs.createReadStream(inputPath) .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(outputPath)) 更好的方式是使用 stream.pipeline const stream = require('stream') const util = requir

thankseveryday的专栏 1034
上一篇: NodeJS 树结构遍历 —— 深度优先和广度优先
下一篇: NodeJS —— 流的原理分析与简易实现
Panda Shen
博客等级 码龄10年 18粉丝 50原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值