Node.js中Stream模块详解

Node.js 中 Stream 模块全部 API 详解

一、Stream 基础概念

javascript 复制代码
const { Stream } = require('stream');

// 1. Stream 类型
// - Readable: 可读流
// - Writable: 可写流
// - Duplex: 双工流
// - Transform: 转换流

// 2. Stream 事件
// - data: 数据可读时触发
// - end: 数据读取完成时触发
// - error: 发生错误时触发
// - finish: 数据写入完成时触发
// - close: 流关闭时触发
// - pipe: 流被管道连接时触发
// - unpipe: 流取消管道连接时触发

二、Readable 流

javascript 复制代码
const { Readable } = require('stream');

// 1. 创建可读流
class MyReadable extends Readable {
  constructor(options) {
    super(options);
    this.data = ['a', 'b', 'c'];
  }

  _read() {
    const chunk = this.data.shift();
    if (chunk) {
      this.push(chunk);
    } else {
      this.push(null);  // 结束流
    }
  }
}

// 2. 使用可读流
const readable = new MyReadable();
readable.on('data', (chunk) => {
  console.log('收到数据:', chunk);
});
readable.on('end', () => {
  console.log('数据读取完成');
});

// 3. 暂停和恢复
readable.pause();  // 暂停读取
readable.resume();  // 恢复读取

// 4. 销毁流
readable.destroy();  // 销毁流

// 5. 设置编码
readable.setEncoding('utf8');  // 设置编码

// 6. 取消管道连接
readable.unpipe();  // 取消所有管道连接
readable.unpipe(writable);  // 取消特定管道连接

// 7. 检查流状态
console.log('是否可读:', readable.readable);
console.log('是否暂停:', readable.isPaused());

三、Writable 流

javascript 复制代码
const { Writable } = require('stream');

// 1. 创建可写流
class MyWritable extends Writable {
  constructor(options) {
    super(options);
  }

  _write(chunk, encoding, callback) {
    console.log('写入数据:', chunk);
    callback();  // 写入完成
  }

  _final(callback) {
    console.log('写入完成');
    callback();
  }
}

// 2. 使用可写流
const writable = new MyWritable();
writable.write('Hello');  // 写入数据
writable.end('World');  // 写入数据并结束流

// 3. 设置编码
writable.setDefaultEncoding('utf8');  // 设置默认编码

// 4. 检查流状态
console.log('是否可写:', writable.writable);
console.log('是否已结束:', writable.writableEnded);
console.log('是否已完成:', writable.writableFinished);

// 5. 销毁流
writable.destroy();  // 销毁流

// 6. 清空缓冲区
writable.cork();  // 暂停写入
writable.uncork();  // 恢复写入

四、Duplex 流

javascript 复制代码
const { Duplex } = require('stream');

// 1. 创建双工流
class MyDuplex extends Duplex {
  constructor(options) {
    super(options);
    this.data = ['a', 'b', 'c'];
  }

  _read() {
    const chunk = this.data.shift();
    if (chunk) {
      this.push(chunk);
    } else {
      this.push(null);
    }
  }

  _write(chunk, encoding, callback) {
    console.log('写入数据:', chunk);
    callback();
  }
}

// 2. 使用双工流
const duplex = new MyDuplex();
duplex.on('data', (chunk) => {
  console.log('收到数据:', chunk);
});
duplex.write('Hello');
duplex.end();

五、Transform 流

javascript 复制代码
const { Transform } = require('stream');

// 1. 创建转换流
class MyTransform extends Transform {
  constructor(options) {
    super(options);
  }

  _transform(chunk, encoding, callback) {
    // 转换数据
    const transformed = chunk.toString().toUpperCase();
    this.push(transformed);
    callback();
  }

  _flush(callback) {
    // 流结束时的处理
    this.push('END');
    callback();
  }
}

// 2. 使用转换流
const transform = new MyTransform();
transform.on('data', (chunk) => {
  console.log('转换后数据:', chunk);
});
transform.write('hello');
transform.end();

六、管道操作

javascript 复制代码
const { Readable, Writable } = require('stream');

// 1. 基本管道操作
readable.pipe(writable);

// 2. 链式管道
readable
  .pipe(transform1)
  .pipe(transform2)
  .pipe(writable);

// 3. 管道事件
readable.on('pipe', (source) => {
  console.log('开始管道连接');
});
writable.on('unpipe', (source) => {
  console.log('取消管道连接');
});

// 4. 错误处理
readable.on('error', (err) => {
  console.error('读取错误:', err);
});
writable.on('error', (err) => {
  console.error('写入错误:', err);
});

七、实用工具函数

javascript 复制代码
const { pipeline, finished } = require('stream');

// 1. pipeline 函数
pipeline(
  readable,
  transform,
  writable,
  (err) => {
    if (err) {
      console.error('管道错误:', err);
    } else {
      console.log('管道完成');
    }
  }
);

// 2. finished 函数
finished(readable, (err) => {
  if (err) {
    console.error('流结束错误:', err);
  } else {
    console.log('流正常结束');
  }
});

八、实际应用示例

javascript 复制代码
const { Readable, Writable, Transform } = require('stream');
const fs = require('fs');
const zlib = require('zlib');

// 1. 文件流操作
// 读取文件
const readStream = fs.createReadStream('input.txt');
// 写入文件
const writeStream = fs.createWriteStream('output.txt');
// 管道连接
readStream.pipe(writeStream);

// 2. 压缩文件
const gzip = zlib.createGzip();
fs.createReadStream('input.txt')
  .pipe(gzip)
  .pipe(fs.createWriteStream('output.txt.gz'));

// 3. 解压文件
const gunzip = zlib.createGunzip();
fs.createReadStream('input.txt.gz')
  .pipe(gunzip)
  .pipe(fs.createWriteStream('output.txt'));

// 4. 自定义转换流
class UppercaseTransform extends Transform {
  _transform(chunk, encoding, callback) {
    this.push(chunk.toString().toUpperCase());
    callback();
  }
}

// 5. 流式数据处理
const processData = new UppercaseTransform();
fs.createReadStream('input.txt')
  .pipe(processData)
  .pipe(fs.createWriteStream('output.txt'));

// 6. 错误处理
processData.on('error', (err) => {
  console.error('处理错误:', err);
});

// 7. 流控制
let paused = false;
processData.on('data', (chunk) => {
  if (paused) {
    processData.pause();
  }
});

// 8. 内存管理
const CHUNK_SIZE = 1024 * 1024;  // 1MB
fs.createReadStream('large-file.txt', { highWaterMark: CHUNK_SIZE })
  .pipe(fs.createWriteStream('output.txt'));

九、高级特性

javascript 复制代码
const { Readable, Writable, Transform } = require('stream');

// 1. 背压处理
class BackpressureWritable extends Writable {
  constructor(options) {
    super(options);
    this.highWaterMark = options.highWaterMark || 16384;
  }

  _write(chunk, encoding, callback) {
    // 检查缓冲区大小
    if (this.writableLength >= this.highWaterMark) {
      // 暂停读取
      this.emit('drain');
      callback(new Error('背压'));
    } else {
      // 继续写入
      callback();
    }
  }
}

// 2. 自定义缓冲区
class CustomBuffer extends Transform {
  constructor(options) {
    super(options);
    this.buffer = Buffer.alloc(0);
  }

  _transform(chunk, encoding, callback) {
    // 添加到缓冲区
    this.buffer = Buffer.concat([this.buffer, chunk]);
    
    // 处理完整的数据块
    while (this.buffer.length >= 1024) {
      const data = this.buffer.slice(0, 1024);
      this.buffer = this.buffer.slice(1024);
      this.push(data);
    }
    
    callback();
  }

  _flush(callback) {
    // 处理剩余数据
    if (this.buffer.length > 0) {
      this.push(this.buffer);
    }
    callback();
  }
}

// 3. 流组合
class StreamComposer extends Transform {
  constructor(streams) {
    super();
    this.streams = streams;
  }

  _transform(chunk, encoding, callback) {
    // 处理多个流
    let result = chunk;
    for (const stream of this.streams) {
      result = stream._transform(result, encoding);
    }
    this.push(result);
    callback();
  }
}

// 4. 流监控
class StreamMonitor extends Transform {
  constructor(options) {
    super(options);
    this.stats = {
      bytesProcessed: 0,
      chunksProcessed: 0,
      startTime: Date.now()
    };
  }

  _transform(chunk, encoding, callback) {
    // 更新统计信息
    this.stats.bytesProcessed += chunk.length;
    this.stats.chunksProcessed++;
    
    // 输出统计信息
    console.log('处理统计:', this.stats);
    
    this.push(chunk);
    callback();
  }
}

十、最佳实践

javascript 复制代码
const { Readable, Writable, Transform } = require('stream');

// 1. 错误处理
function handleStreamError(stream, error) {
  console.error('流错误:', error);
  stream.destroy();
}

// 2. 资源清理
function cleanupStream(stream) {
  stream.removeAllListeners();
  stream.destroy();
}

// 3. 性能优化
function optimizeStream(stream, options) {
  stream.setMaxListeners(options.maxListeners || 10);
  stream.setEncoding(options.encoding || 'utf8');
}

// 4. 流控制
function controlStream(stream, options) {
  const { highWaterMark, lowWaterMark } = options;
  
  stream.on('drain', () => {
    console.log('缓冲区已清空');
  });
  
  stream.on('error', (err) => {
    console.error('流错误:', err);
  });
}

// 5. 流组合
function composeStreams(streams) {
  return streams.reduce((prev, curr) => {
    return prev.pipe(curr);
  });
}

// 6. 流监控
function monitorStream(stream) {
  const stats = {
    startTime: Date.now(),
    bytesProcessed: 0,
    chunksProcessed: 0
  };
  
  stream.on('data', (chunk) => {
    stats.bytesProcessed += chunk.length;
    stats.chunksProcessed++;
  });
  
  stream.on('end', () => {
    const duration = Date.now() - stats.startTime;
    console.log('流统计:', {
      ...stats,
      duration,
      throughput: stats.bytesProcessed / duration
    });
  });
  
  return stats;
}

Stream 模块的主要特点:

  1. 提供流式数据处理能力
  2. 支持多种流类型
  3. 提供丰富的操作方法
  4. 高效的内存管理
  5. 支持背压处理

使用建议:

  1. 正确处理错误
  2. 注意内存使用
  3. 合理使用背压
  4. 及时清理资源
  5. 监控流性能
相关推荐
小李李33 小时前
基于Node.js的Web爬虫: 使用Axios和Cheerio抓取网页数据
前端·爬虫·node.js·跨域
:-)13 小时前
macOS上管理多个Node.js版本
macos·node.js
夏子曦15 小时前
webpack 的工作流程
前端·webpack·node.js
thinkQuadratic1 天前
使用node.js来实现SSE服务端向客户端推送消息
node.js
撸码到无法自拔2 天前
使用 MCP(模型上下文协议)和 Claude 在 Node.js 中构建聊天应用程序
node.js
Dontla2 天前
node.js模块化步骤(各标准区别)CommonJS规范、AMD规范、UMD规范、ES Modules (ESM)
node.js
正在努力Coding2 天前
Cannot find module ‘@npmcli/config‘
node.js
m0_zj2 天前
55.[前端开发-前端工程化]Day02-包管理工具npm等
前端·npm·node.js
zlpzlpzyd2 天前
node.js为什么产生?
node.js
大G哥3 天前
使用 MCP(模型上下文协议)和 Claude 在 Node.js 中构建聊天应用程序
node.js