可读流工作原理
- 数据读取方式:可读流用于从数据源(如文件、网络连接等)读取数据。它有两种模式:流动模式和暂停模式。
- 流动模式:数据会自动从底层系统流向应用程序,通过
data
事件来处理数据块。一旦流进入流动模式,它会不断触发data
事件,直到没有数据可读。例如:
const fs = require('fs');
const readableStream = fs.createReadStream('example.txt');
readableStream.on('data', (chunk) => {
console.log('Received a chunk of data:', chunk.length);
});
- **暂停模式**:需要手动调用`read()`方法来读取数据块。流在创建时默认处于暂停模式。在暂停模式下,数据不会自动流动,只有调用`read()`方法时才会读取数据。例如:
const fs = require('fs');
const readableStream = fs.createReadStream('example.txt');
readableStream.pause();
setTimeout(() => {
const chunk = readableStream.read(1024); // 读取1024字节的数据
if (chunk) {
console.log('Read a chunk of data:', chunk.length);
}
}, 1000);
- 内部缓冲区:可读流内部维护一个缓冲区,用于暂存从数据源读取的数据。当缓冲区满或者达到一定条件时,会触发
data
事件。应用程序处理数据的速度可能与数据流入缓冲区的速度不同,通过highWaterMark
属性可以设置缓冲区的大小,默认值为64KB(对于fs.ReadStream
)。
可写流工作原理
- 数据写入方式:可写流用于将数据写入到目的地(如文件、网络连接等)。通过
write()
方法将数据写入流,数据会先进入内部缓冲区。例如:
const fs = require('fs');
const writableStream = fs.createWriteStream('output.txt');
const data = 'Hello, World!';
const writeResult = writableStream.write(data);
if (!writeResult) {
console.log('Buffer is full, need to wait before writing more data.');
}
- 背压处理:当可写流的内部缓冲区已满,而还有数据要写入时,就会出现背压问题。此时,
write()
方法会返回false
,通知调用者暂时停止写入数据。应用程序需要监听drain
事件,当缓冲区有空间时,drain
事件会被触发,此时可以继续写入数据。例如:
const fs = require('fs');
const writableStream = fs.createWriteStream('output.txt');
let data = 'a'.repeat(1024 * 1024); // 大量数据
const writeSomeData = () => {
while (data.length > 0) {
const writeResult = writableStream.write(data.slice(0, 1024));
if (!writeResult) {
break;
}
data = data.slice(1024);
}
};
writeSomeData();
writableStream.on('drain', () => {
console.log('Buffer has drained, can write more data.');
writeSomeData();
});
使用pipe方法优化读写操作
- 原理:
pipe
方法将可读流和可写流连接起来,实现数据的自动流动,从可读流读取数据并写入到可写流。它会自动处理背压问题,使得数据从可读流平稳地流向可写流,而无需手动处理缓冲区满等复杂情况。例如:
const fs = require('fs');
const readableStream = fs.createReadStream('input.txt');
const writableStream = fs.createWriteStream('output.txt');
readableStream.pipe(writableStream);
- 优势:
- 简化代码:无需手动监听
data
、drain
等事件,大大减少了代码量,使读写操作更简洁。
- 高效处理:自动处理背压,保证数据在不同速度的可读流和可写流之间高效传输,避免数据丢失或缓冲区溢出等问题。