How to Handle Large File Uploads with the express-fileupload Middleware in Node.js
Use the express-fileupload middleware with useTempFiles: true and set appropriate limits to stream large files to disk instead of buffering them in memory, preventing Node.js heap exhaustion.
The expressjs/express repository provides a robust web framework, but its core intentionally excludes multipart file handling to keep the footprint minimal. To handle large file uploads with the express fileupload middleware, you must install the third-party package and configure it to stream data to temporary files rather than storing it in RAM.
Why Express Core Doesn't Handle File Uploads
Express core only ships with generic body-parsing middleware and does not provide a built-in file-upload handler. In lib/express.js, the framework exports json, raw, text, and urlencoded parsers, but omits any multipart or file handling capabilities:
// From lib/express.js (lines 77-82)
exports.json = require('body-parser').json;
exports.raw = require('body-parser').raw;
exports.text = require('body-parser').text;
exports.urlencoded = require('body-parser').urlencoded;
This architectural decision keeps the core lightweight. For file uploads, you must integrate a dedicated middleware like express-fileupload, multer, or busboy that can manage binary streams, temporary storage, and size constraints.
Installing and Configuring express-fileupload
Installation
Add the package to your project:
npm install express-fileupload
Middleware Configuration for Large Files
Mount the middleware early in your application stack with options optimized for large payloads. The critical setting is useTempFiles, which forces the middleware to write upload streams to disk rather than buffering them in memory.
const express = require('express');
const fileUpload = require('express-fileupload');
const path = require('path');
const app = express();
app.use(
fileUpload({
// Stream to disk instead of RAM
useTempFiles: true,
tempFileDir: path.join(__dirname, 'tmp'),
// Set appropriate size limits (e.g., 2 GB)
limits: { fileSize: 2 * 1024 * 1024 * 1024 },
// Return 413 Payload Too Large when exceeded
abortOnLimit: true,
// Sanitize filenames
safeFileNames: true,
preserveExtension: true,
})
);
Key configuration parameters:
- useTempFiles: When
true, files stream totempFileDirinstead of residing in memory as buffers - limits.fileSize: Hard limit in bytes; requests exceeding this trigger immediate termination
- abortOnLimit: Ends the request with HTTP 413 when size limits are breached
Processing Uploaded Files in Routes
Once configured, uploaded files populate req.files as objects containing metadata and helper methods. Access the file via the input field name and move it to permanent storage:
app.post('/upload', (req, res) => {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
// Access file by input name (e.g., <input name="document">)
const uploadedFile = req.files.document;
const targetPath = path.join(__dirname, 'uploads', uploadedFile.name);
// Move from temp directory to final destination
uploadedFile.mv(targetPath, (err) => {
if (err) return res.status(500).send(err);
res.send('File uploaded successfully');
});
});
The mv() method handles the filesystem operation, moving the file from the temporary directory specified in tempFileDir to your target location.
Production Considerations for Large Uploads
Memory Management
Always set useTempFiles: true when handling files larger than a few megabytes. Without this option, express-fileupload stores the entire file in req.files.<name>.data as a Buffer, easily exhausting the V8 heap on multi-gigabyte uploads.
Timeout and Server Limits
Large uploads over slow connections may exceed default HTTP timeouts. Configure your server to accommodate extended transfer times:
const server = app.listen(3000);
// Disable timeout for long uploads (0 = no timeout)
server.setTimeout(0);
server.maxHeadersCount = 2000;
Additionally, increase the OS file-descriptor limit (ulimit -n) to support many concurrent upload streams without hitting "EMFILE, too many open files" errors.
Reverse Proxy Configuration
When using Nginx or Apache as a reverse proxy, ensure client_max_body_size (Nginx) or LimitRequestBody (Apache) matches or exceeds your express-fileupload limits. However, keep proxy buffers modest to maintain streaming behavior to your Node.js process rather than buffering the entire request at the proxy layer.
Alternative: Streaming with busboy
For ultimate control over streaming—such as piping directly to cloud storage without touching disk—you can use busboy directly. This approach bypasses temporary files entirely:
const Busboy = require('busboy');
const fs = require('fs');
const path = require('path');
app.post('/upload', (req, res) => {
const busboy = new Busboy({
headers: req.headers,
limits: { fileSize: 10 * 1024 * 1024 * 1024 } // 10 GB
});
let saved = false;
busboy.on('file', (fieldname, file, filename) => {
const outPath = path.join(__dirname, 'uploads', path.basename(filename));
const writeStream = fs.createWriteStream(outPath);
file.pipe(writeStream);
writeStream.on('close', () => (saved = true));
});
busboy.on('finish', () => {
saved ? res.send('File saved') : res.status(400).send('Upload failed');
});
req.pipe(busboy);
});
Summary
- Express core (
lib/express.js) exports onlyjson,raw,text, andurlencodedparsers, requiring third-party middleware for file handling. - express-fileupload handles large files safely when configured with
useTempFiles: trueand appropriatetempFileDirsettings. - Always set size limits via the
limitsoption and enableabortOnLimitto prevent denial-of-service attacks. - Use streaming approaches (either via
express-fileuploadtemp files or directbusboypiping) to avoid memory exhaustion on multi-gigabyte uploads. - Configure server timeouts and OS limits to support long-running upload connections in production environments.
Frequently Asked Questions
Does Express have built-in file upload support?
No. According to the expressjs/express source code in lib/express.js, the framework only provides built-in body parsers for JSON, raw text, and URL-encoded data. File uploads require external middleware such as express-fileupload, multer, or busboy.
What is the maximum file size for express-fileupload?
There is no hardcoded maximum; you define the limit via the limits.fileSize option (in bytes). You can set this to any value your filesystem and network can support, though practical limits depend on available disk space and server memory configuration.
How do I prevent memory crashes during large uploads?
Set useTempFiles: true in your middleware configuration. This streams incoming files to the directory specified in tempFileDir rather than buffering them as Buffers in memory. Without this setting, uploads larger than the Node.js heap limit will crash your application.
Can I stream files directly to cloud storage without saving locally?
Yes, but not with express-fileupload's default behavior. Instead, use the busboy library directly to intercept the file stream and pipe it to cloud storage SDKs (AWS S3, Google Cloud Storage, etc.) without writing to local disk first. This reduces I/O overhead but requires more complex stream handling.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →