Node.js 流(Streams)基础:读写、管道与链接详解

作者:接口漫谈

时间:2026-10-30

浏览:647

本文深入解析 Node.js 中的流(Streams)机制,涵盖可读流与可写流的核心概念。详细讲解如何通过 data 事件和 read() 方法读取数据,设置编码格式,以及利用 pipe() 和链接(Chaining)高效处理文件传输与解压任务,帮助开发者掌握异步 I/O 的最佳实践。

文章目录

Node.js 流概述

Node.js 采用异步和事件驱动架构,这使其在处理 I/O 密集型任务时表现优异。当应用涉及大量 I/O 操作时,利用 Node.js 提供的流(Stream)机制可以显著简化开发流程。接下来,我们将深入探讨流的工作原理及其如何优化 I/O 处理。

可读流基础

流的概念 流类似于 Unix 系统中的管道,允许用户从数据源轻松读取数据并将其传输至其他目的地。 本质上,流并非特殊实体,而是实现了特定方法的 EventEmitter。根据所实现的方法不同,流可分为可读流(Readable)、可写流(Writable)或双向流(Duplex,兼具读写功能)。 可读流用于从数据源读取数据,而可写流则用于向目标写入数据。 在使用 Node.js 的过程中,你可能已经接触过流。例如,在 HTTP 服务器中,request 对象是一个可读流,而 response 对象是一个可写流。此外,fs 模块也提供了处理读写流的功能。 本文将重点介绍可读流和可写流的基础知识,双向流因超出范围暂不讨论。 可读流 (Readable Streams) 通过可读流,你可以从各种数据源(如文件、内存中的 Buffer 或其他流)读取数据。由于流是 EventEmitter,它们通过触发不同事件来传递数据,我们可以利用这些事件来控制流的运行。

从流中读取数据

从流中读取数据 监听 data 事件并添加回调函数是从可读流中获取数据的最佳方式。当有数据可用时,流会触发 data 事件,进而执行回调函数。请参考以下代码示例:


 var fs = require('fs');
var readableStream = fs.createReadStream('file.txt');
var data = '';

var readableStream.on('data', function(chunk){
 data += chunk;
});

readableStream.on('end', function(){
 console.log(data);
});
 

fs.createReadStream 方法返回一个可读流。 初始状态下,流处于非流动状态。只有当添加了 data 事件监听器及回调函数后,流才会开始流动。此后,流会读取小块数据并传递给回调函数。 流的实现者决定了 data 事件的触发频率。例如,HTTP request 在读取几 KB 数据时触发 data 事件;而从文件读取时,可根据需求设定在每行结束时触发。 当数据读取完毕(到达文件末尾)时,流会触发 end 事件。在上述示例中,我们监听该事件,并在文件读取完成后打印数据。 另一种读取流的方式是在到达文件末尾前,不断调用流实例的 read() 方法。


 var fs = require('fs');
var readableStream = fs.createReadStream('file.txt');
var data = '';
var chunk;

readableStream.on('readable', function(){
 while ((chunk = readableStream.read()) != null) {
 data += chunk;
 }
});

readableStream.on('end', function(){
 console.log(data);
});

设置编码

read() 方法从内部 Buffer 中读取数据,若无数据可读,则返回 null。 因此,在 while 循环中检查 read() 的返回值,当其为 null 时终止循环。 值得注意的是,当有数据可读时,会触发 readable 事件。 设置编码 默认情况下,从流中读取的是 Buffer 对象。若需处理字符串,这并不适用。因此,可以通过调用 Readable.setEncoding() 来设置流的编码格式,如下例所示:


 var fs = require('fs');
var readableStream = fs.createReadStream('file.txt');
var data = '';

readableStream.setEncoding('utf8');

readableStream.on('data', function(chunk){
 data += chunk;
});

readableStream.on('end', function(){
 console.log(data);
});

管道与链接操作

在上述示例中,我们将编码设置为 utf8,数据将被解析为 utf8 格式,回调函数中的 chunk 参数即为字符串。 管道 (Piping) 管道是一种高效的机制,无需手动管理流状态即可从数据源读取并写入目的地。先看以下示例:


 var fs = require('fs');
var readableStream = fs.createReadStream('file1.txt');
var writableStream = fs.createWriteStream('file2.txt');

readableStream.pipe(writableStream);

上述示例利用 pipe() 方法将 file1 的内容写入 file2。由于 pipe() 自动管理数据流,无需担心数据流速问题,这使得操作简洁易用。 需要注意的是,pipe() 返回目标流,因此可以轻松地将多个流链接起来! 链接 (Chaining) 假设有一个归档文件需要解压。有多种方式可完成此任务,但最简洁的方式是利用管道和链接:


 var fs = require('fs');
var zlib = require('zlib');

fs.createReadStream('input.txt.gz')
 .pipe(zlib.createGunzip())
 .pipe(fs.createWriteStream('output.txt'));

首先,通过 input.txt.gz 创建可读流,然后将其传递给 zlib.createGunzip() 流以解压内容。最后,添加一个可写流将解压后的内容写入另一个文件。 其他方法 我们已讨论了一些可读流中的重要概念,以下是其他需要注意的方法: 1.Readable.pause() – 暂停流的流动,即不再触发 data 事件。 2.Readable.resume() – 与 pause() 相反,使暂停的流恢复流动。 3.Readable.unpipe() – 移除目标流。若传入参数,则停止流向特定目标;否则移除所有目标。

可写流详解

可写流 (Writable Streams) 可写流用于将数据写入目的地。与可读流类似,它们也是 EventEmitter,会触发不同事件。让我们看看可写流中涉及的事件和方法。 写入数据 要向可写流写入数据,需调用可写流实例的 write() 方法,参考以下示例:


 var fs = require('fs');
var readableStream = fs.createReadStream('file1.txt');
var writableStream = fs.createWriteStream('file2.txt');

readableStream.setEncoding('utf8');

readableStream.on('data', function(chunk){
 writableStream.write('chunk');
});

上述代码非常简单,它从输入流读取数据,并使用 write() 写入目的地。 该方法返回布尔值以表示写入是否成功。若返回 true,表示写入成功,可继续写入更多数据;若返回 false,则表示发生错误,当前无法继续写入。此时,可写流会触发 drain 事件,提示你可以继续写入数据。 完成写入 当不再需要写入数据时,可调用 end() 方法告知流写入已完成。假设 res 是 HTTP response 对象,通常需向浏览器发送响应: res.write('Some Data!!'); res.end(); 调用 end() 时,所有数据将被写入,随后流触发 finish 事件。注意,调用 end() 后,不能再向可写流写入数据。例如以下代码会报错: res.write('Some Data!!'); res.end(); res.write('Trying to write again'); //Error ! 以下是与可写流相关的重要事件: 1.error – 写入或链接发生错误时触发 2.pipe – 当可读流链接到可写流时触发 3.unpipe – 当可读流调用 unpipe 时触发

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章