Node.js Stream Module
Stream is an abstract interface in Node.js for handling streaming data. You can think of a stream as a water flow, where data flows from one place to another like water. This processing approach is especially suitable for large files or continuous data sources, because it does not need to load all data into memory at once.
Why Do We Need Stream?
- Memory efficiency: When processing large files, there is no need to load the entire file into memory at once
- Time efficiency: Data can be processed as it is received, without waiting for all data to be ready
- Composability: Multiple streams can be connected like pipes to form a data processing pipeline
Four Basic Types of Stream
1. Readable Stream
A readable stream is the source of data, from which data can be read. For example, reading content from a file or obtaining data from an HTTP request.
Example
const readableStream = fs.createReadStream('example.txt');
readableStream.on('data', (chunk) => {
console.log(`Received ${chunk.length}bytes of data`);
});
readableStream.on('end', () => {
console.log('No more data');
});
2. Writable Stream
A writable stream is the destination of data, into which data can be written. For example, writing to a file or sending an HTTP response.
Example
const writableStream = fs.createWriteStream('output.txt');
writableStream.write('First line of data\n');
writableStream.write('Second line of data\n');
writableStream.end('Last line of data');
3. Duplex Stream
A duplex stream is both readable and writable, such as a TCP socket.
Example
const myDuplex = new Duplex({
write(chunk, encoding, callback) {
console.log(chunk.toString());
callback();
},
read(size) {
this.push('Data read from the duplex stream');
this.push(null); // Indicates the end of data
}
});
4. Transform Stream
A transform stream is a special type of duplex stream that can modify or transform data during the reading and writing process. For example, compressing or encrypting data.
Example
const upperCaseTr = new Transform({
transform(chunk, encoding, callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
});
process.stdin.pipe(upperCaseTr).pipe(process.stdout);
Common Methods of Stream
pipe() Method
pipe()The method is one of the most powerful features of Stream; it can connect multiple streams together to form a pipeline.
Example
// Create a readable stream and a writable stream
const readStream = fs.createReadStream('input.txt');
const writeStream = fs.createWriteStream('output.txt');
// Use pipe to connect the readable stream and the writable stream
readStream.pipe(writeStream);
writeStream.on('finish', () => {
console.log('Data write complete');
});
Event Listening
Stream is an instance of EventEmitter. Various situations can be handled by listening to events:
data- Triggered when data is available to readend- Triggered when there is no more data to readerror- Triggered when an error occursfinish- Triggered when all data has been flushed to the underlying system
Practical Application Examples
Example 1: File Copy
Example
function copyFile(source, target, cb) {
// Create read and write streams
const rd = fs.createReadStream(source);
const wr = fs.createWriteStream(target);
// Handle errors
rd.on('error', err => cb(err));
wr.on('error', err => cb(err));
// Completion callback
wr.on('finish', cb);
// Start copying
rd.pipe(wr);
}
// Usage
copyFile('source.txt', 'target.txt', err => {
if (err) console.error(err);
else console.log('Copy complete');
});
Example 2: HTTP Server Compressing Files
Example
const http = require('http');
const zlib = require('zlib');
http.createServer((req, res) => {
// Create a readable stream
const readStream = fs.createReadStream('./largeFile.txt');
// Set response headers
res.writeHead(200, {
'Content-Type': 'text/plain',
'Content-Encoding': 'gzip'
});
// Pipeline chain: read -> compress -> respond
readStream.pipe(zlib.createGzip()).pipe(res);
}).listen(3000);
Stream Best Practices
- Error handling: Always add error handling listeners to streams
- Memory management: For large files, use streams instead of reading everything at once
- Pipeline chain: Use the pipe() method appropriately to combine multiple streams
- Backpressure handling: When the write speed cannot keep up with the read speed, backpressure needs to be handled appropriately
Error Handling Example
Example
const readStream = fs.createReadStream('not-exist-file.txt');
readStream.on('error', (err) => {
console.error('Error occurred:', err.message);
});
By mastering the Node.js Stream module, you can efficiently handle various I/O operations. Especially when processing large files or real-time data, streams will become your powerful tool.
Other Extensions
Node.js Built-in Modules