Difficulty: Advanced
Explain Node.js streams. Why are they important for handling large files?
Streams process data piece by piece (chunks) instead of loading everything into memory. This is essential for handling large files, network data, or real-time processing.
Stream types: 1. Readable: source of data (fs.createReadStream, http request) 2. Writable: destination (fs.createWriteStream, http response) 3. Duplex: both readable and writable (TCP socket) 4. Transform: modify data as it passes through (zlib.createGzip)
Key advantage: A 10GB file can be processed with ~64KB of memory using streams.
const fs = require('fs');
// BAD: loads entire file into memory
// For a 2GB file, this uses 2GB+ RAM
fs.readFile('huge.csv', 'utf8', (err, data) => {
const lines = data.split('\n');
console.log(`Lines: ${lines.length}`);
});
// GOOD: stream processes line by line
// Uses only a few KB of memory
const readline = require('readline');
const rl = readline.createInterface({
input: fs.createReadStream('huge.csv'),
});
let count = 0;
rl.on('line', (line) => {
count++;
});
rl.on('close', () => {
console.log(`Lines: ${count}`);
});
fs.readFile loads the entire file. Streams process it chunk by chunk, keeping memory usage constant regardless of file size.
const fs = require('fs');
const zlib = require('zlib');
// Compress a file using pipe
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'))
.on('finish', () => console.log('Compressed!'));
// HTTP response streaming
const http = require('http');
http.createServer((req, res) => {
const stream = fs.createReadStream('bigfile.mp4');
// Pipe directly to response - no memory buildup
stream.pipe(res);
}).listen(3000);
pipe() connects streams together and handles backpressure automatically. Data flows through the chain without buffering the entire content.
const { Transform } = require('stream');
// Transform that converts text to uppercase
const upperCase = new Transform({
transform(chunk, encoding, callback) {
const upper = chunk.toString().toUpperCase();
callback(null, upper);
},
});
// Usage
process.stdin
.pipe(upperCase)
.pipe(process.stdout);
// Or with file
fs.createReadStream('input.txt')
.pipe(upperCase)
.pipe(fs.createWriteStream('output.txt'));
Transform streams let you process and modify data in transit. The callback(null, data) pattern passes transformed data to the next stream.
Readable Stream, Writable Stream, Transform Stream, Pipe, Buffer, Backpressure