In this lesson, you will learn how to handle large file uploads by processing multipart form data as a stream rather than buffering it into memory.
The Problem with Traditional Uploads
When a user uploads a large file (like a 2GB video) via an HTTP POST request, many developers make the mistake of parsing the entire body into memory. If you try to hold that data in a variable, your Node.js process will quickly run out of memory (Heap limit reached). Instead, we treat the incoming request as a continuous stream of data chunks.
Streaming the Parts
Multipart form data is essentially a single stream broken into different "parts" (the text fields and the files). By using a library like busboy (the industry standard for this), we can tap into the incoming request stream and handle parts as they arrive.
Because we are dealing with files, we must respect backpressure. If the destination (like a cloud storage bucket) is slower than the incoming network stream, we must pause the readable stream so we don't overflow our memory buffers.
Concrete Implementation
Here is how you isolate a file stream from the form data and pipe it directly to a destination:
const Busboy = require('busboy');
// 'req' is our incoming HTTP stream
const busboy = Busboy({ headers: req.headers });
busboy.on('file', (fieldname, fileStream, info) => {
// 'fileStream' is a readable stream of the file content.
// We pipe it to an output stream (e.g., a file system write stream)
// The 'pipe' method handles backpressure automatically!
const saveTo = fs.createWriteStream(`./uploads/${info.filename}`);
fileStream.pipe(saveTo);
});
req.pipe(busboy);
By piping the fileStream directly to a writable stream, the Node.js stream API monitors the state of both. If the file system cannot keep up with the network download, the busboy parser will signal the network source to pause, effectively preventing memory bloat. This allows you to handle files of virtually any size, regardless of your server's available RAM.
Key Takeaway: Never parse large multipart uploads into memory; pipe them directly from the request stream to their destination to leverage built-in backpressure management.