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. 监控流性能
相关推荐
魔云连洲3 小时前
用Webpack 基础配置快速搭建项目开发环境
前端·webpack·node.js
石页玩逆向3 小时前
某企查-某度旋转验证码V2算法分析
node.js
·薯条大王4 小时前
Node.js 模块包的管理和使用是
node.js
熊猫片沃子6 小时前
使用nvm解决nodejs多版本问题,难道不香吗?
前端·npm·node.js
Mintopia6 小时前
Node.js 与前端现代工具链进阶指南
前端·javascript·node.js
孟陬8 小时前
Node.js 如何检测 script 脚本是在项目本身运行
node.js·bun
玲小珑10 小时前
5分钟学会LLM开发系列教程(五)Agent的最终形态LangGraph
langchain·node.js·ai编程
林太白11 小时前
Nest如何连接数据库
前端·后端·node.js
A-Kamen19 小时前
Webpack vs Vite:深度对比与实战示例,如何选择最佳构建工具?
前端·webpack·node.js
OpenTiny社区1 天前
Node.js技术原理分析系列7——Node.js模块加载方式分析
前端·node.js