Every fs.readFile() call so far loads the entire file into memory before your code can touch any of it. That's fine for a small text file. For a 4 GB video, it means holding all 4 GB in memory at once just to start processing it. Streams solve this by handling data in small pieces, as those pieces arrive, instead of all at once.
const fs = require('fs')
const stream = fs.createReadStream('huge-file.txt', 'utf8')
stream.on('data', (chunk) => {
console.log(`Received ${chunk.length} characters`)
})
stream.on('end', () => {
console.log('Finished reading the file')
})
stream.on('error', (err) => {
console.error('Something went wrong:', err)
})This is EventEmitter from a few lessons back, applied directly — a readable stream is an EventEmitter that emits 'data' for every chunk and 'end' once the file is fully read.
const writeStream = fs.createWriteStream('output.txt')
writeStream.write('First line\n')
writeStream.write('Second line\n')
writeStream.end()Reading from one stream and writing each chunk to another is common enough to have its own shortcut: .pipe().
const readStream = fs.createReadStream('input.txt')
const writeStream = fs.createWriteStream('output.txt')
readStream.pipe(writeStream)This one line copies a file of any size, at any speed the disk can manage, without ever holding the whole thing in memory. It's the same mechanism http uses under the hood — req and res in the web server lessons are themselves streams.
| Situation | Worth streaming? |
|---|---|
| A small config file (a few KB) | No — readFile is simpler and the difference is unmeasurable |
| A large video or log file | Yes — loading it whole could exhaust available memory |
| Serving a file over an HTTP response | Yes — the browser can start rendering before the whole file arrives |
Streams are one of the areas where Node's design shows through most clearly: memory is a shared, limited resource, and a server handling many requests at once can't afford to load a large file into memory for each one.