NodeJS文件流

434 阅读6分钟

Node.js 的文件流(File Streams)是非常强大且高效的工具,特别适用于处理大文件、流式数据或需要高效文件操作的场景。fs 模块提供了流(Stream)接口,允许你以异步、非阻塞的方式处理文件的读取和写入。

在 Node.js 中,流分为 读流写流,它们的核心是通过流动的方式将数据传输处理,而不是一次性加载整个文件。这能显著减少内存使用并提升性能。

流(Stream)基础

流是一种处理数据的方式,数据是逐步流动的(而不是一次性加载)。流分为四种类型:

  1. Readable Stream:用于读取数据。
  2. Writable Stream:用于写入数据。
  3. Duplex Stream:既可读又可写。
  4. Transform Stream:对数据进行转换。

文件流通常是 ReadableWritable 类型。

🧱 一、文件流的基本概念

在 Node.js 中,文件流主要通过 fs.createReadStream()fs.createWriteStream() 来操作,它们是基于 stream.Readablestream.Writable 的。

1. 读取文件流 - fs.createReadStream()

fs.createReadStream() 用于从文件中读取数据,并将数据以流的形式逐步传输到内存中。这样可以避免一次性加载整个文件,从而减少内存消耗。

语法

fs.createReadStream(path[, options])
  • path: 文件路径。
  • options: 可选配置项,如编码方式、开始和结束位置等。

以下是 fs.createReadStream(path, options) 的第二个参数(options)的可选配置项总结表:

配置项类型默认值说明
flagsstring'r'文件打开模式,例如 'r' 代表只读
encodingstring 或 nullnull指定读取数据的编码,如 'utf8',不设则返回 Buffer
fdnumbernull已打开文件的描述符,避免重复打开
modenumber0o666设置文件权限(仅在创建时有效)
autoClosebooleantrue流结束时是否自动关闭文件
startnumber0从文件的哪个字节开始读取
endnumbernull读取到哪个字节结束(包含该字节)
highWaterMarknumber64 * 1024每次读取的最大字节数(缓冲区大小)
emitClosebooleanfalse是否在流关闭时触发 'close' 事件

示例:读取文件流

const fs = require('fs');

// 创建一个可读流
const readableStream = fs.createReadStream('example.txt', 'utf8');

// 监听 'data' 事件,逐块读取数据
readableStream.on('data', (chunk) => {
  console.log('读取的数据块:', chunk);
});

// 监听 'end' 事件,表示数据读取完成
readableStream.on('end', () => {
  console.log('文件读取完毕');
});

// 监听 'error' 事件,处理可能的错误
readableStream.on('error', (err) => {
  console.error('读取文件时出错:', err);
});
  • 'data' 事件:每当读取到一个数据块时,data 事件会触发。
  • 'end' 事件:当文件全部读取完成后触发。
  • 'error' 事件:发生错误时触发。

2. 写入文件流 - fs.createWriteStream()

fs.createWriteStream() 用于向文件中写入数据,并将数据以流的方式逐步写入。这种方式特别适合写入大文件或者需要高效写入的场景。

语法

fs.createWriteStream(path[, options])
  • path: 文件路径。
  • options: 可选配置项,类似于 createReadStream()

以下是 fs.createWriteStream(path, options) 的第二个参数(options)的可选配置项总结表:

配置项类型默认值说明
flagsstring'w'文件打开模式,如 'w' 表示写入,必要时创建文件
encodingstring'utf8'写入数据时使用的编码
fdnumbernull已打开的文件描述符,传入时不会再自动打开文件
modenumber0o666文件权限设置(创建文件时适用)
autoClosebooleantrue写入完成或出错时是否自动关闭文件
emitClosebooleanfalse是否在关闭时触发 'close' 事件
startnumberundefined从文件的哪个字节开始写入
highWaterMarknumber16 * 1024内部缓冲区大小,影响 write() 返回值(流控制)

示例:写入文件流

const fs = require('fs');

// 创建一个可写流
const writableStream = fs.createWriteStream('output.txt');

// 向文件写入数据
writableStream.write('这是第一行内容\n');
writableStream.write('这是第二行内容\n');

// 结束写入,关闭流
writableStream.end('写入完成');

// 监听 'finish' 事件,表示写入完成
writableStream.on('finish', () => {
  console.log('数据成功写入文件');
});

// 监听 'error' 事件,处理写入错误
writableStream.on('error', (err) => {
  console.error('写入文件时出错:', err);
});
  • 'finish' 事件:当所有数据成功写入并且流关闭时触发。
  • 'error' 事件:发生写入错误时触发。

3. ### fs.createReadStream 和 fs.createWriteStream 事件总结

事件名触发条件适用对象说明
open文件成功打开createReadStream / createWriteStream当文件被打开时触发,返回文件描述符(fd
close流关闭后触发createReadStream / createWriteStream文件流关闭时触发,表示文件不再被使用
data数据被读取时触发createReadStream每次读取数据时触发,data 事件会传递读取到的数据
end数据读取完毕时触发createReadStream当没有更多数据可读取时触发
finish数据写入完成时触发createWriteStream当数据成功写入流并被刷新到文件时触发
drain内部缓冲区清空后触发createWriteStream当缓冲区的写入数据量少于 highWaterMark,且可继续写入时触发(也就是写入时超过了缓冲区, 等缓冲区清空时就会触发,如果缓冲区一直没有撑满,则不会触发)
error遇到错误时触发createReadStream / createWriteStream读取或写入过程发生错误时触发
pipe流被传递时触发createReadStream当流的数据被“管道”传输到另一个流时触发
unpipe流的管道解除时触发createReadStream当流被解除管道时触发

🛠️ 二、流的管道机制:pipe() 方法

Node.js 提供了流的管道机制,利用 .pipe() 方法可以将一个流直接连接到另一个流。这种方式特别适合文件的复制、数据处理等任务。

示例:使用 .pipe() 复制文件

const fs = require('fs');

// 创建可读流
const readableStream = fs.createReadStream('source.txt');

// 创建可写流
const writableStream = fs.createWriteStream('destination.txt');

// 使用 pipe 直接将源文件的数据流传输到目标文件
readableStream.pipe(writableStream);

// 监听 'finish' 事件,表示复制完成
writableStream.on('finish', () => {
  console.log('文件复制完成');
});

通过 .pipe(),你可以轻松地将数据从一个流传递到另一个流,且自动处理了数据的流动、内存管理等细节。

🧠 三、流的高效处理:缓冲区和背压

1. 背压(Backpressure)

背压是指流速过快时,目标流可能无法及时处理数据的情况。当一个可写流的缓冲区满时,源流会暂停数据的传输,直到目标流有足够空间。这是 Node.js 中流的一个非常重要的特性,有助于避免内存溢出和性能下降。

2. 缓冲区(Buffer)

Node.js 使用缓冲区来处理流中的数据块,缓冲区是一个临时存储区域,用于存储流中的数据块,直到它们被进一步处理。

3. 文件读取写入时产生背压问题以及解决的流程图

deepseek_mermaid_20250619_73afa5.png