How to stream file downloads in Node.js
Streaming file downloads is essential for Node.js applications serving large files efficiently without consuming excessive memory. As the creator of CoreUI with over 11 years of Node.js development experience since 2014, I’ve optimized file serving in numerous enterprise applications. The most effective solution is to use Node.js streams with createReadStream to pipe files directly to the response. This approach handles files of any size efficiently while maintaining low memory usage.
Use createReadStream and pipe to stream file downloads in Node.js.
const express = require('express')
const fs = require('fs')
const path = require('path')
const app = express()
app.get('/download/:filename', (req, res) => {
const filename = req.params.filename
const filepath = path.join(__dirname, 'files', filename)
// Check if file exists
if (!fs.existsSync(filepath)) {
return res.status(404).json({ error: 'File not found' })
}
// Get file stats
const stat = fs.statSync(filepath)
// Set headers
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`)
res.setHeader('Content-Type', 'application/octet-stream')
res.setHeader('Content-Length', stat.size)
// Create read stream and pipe to response
const readStream = fs.createReadStream(filepath)
readStream.on('error', (error) => {
console.error('Stream error:', error)
if (!res.headersSent) {
res.status(500).json({ error: 'Error streaming file' })
}
})
readStream.pipe(res)
})
The createReadStream() creates a readable stream from the file without loading it into memory. The pipe() method automatically handles backpressure and data flow to the response. Setting Content-Disposition triggers browser download behavior with the specified filename. Error handling ensures graceful failure if the stream encounters issues. This approach scales to files of any size without memory concerns.
Best Practice Note
This is the same streaming approach we use in CoreUI backend systems for efficient file serving. For enhanced security, validate and sanitize filenames to prevent path traversal attacks. Consider implementing range request support for resumable downloads and media streaming by checking the Range header and using createReadStream with start and end options.



