# How to Handle Large File Uploads with the express-fileupload Middleware in Node.js

> Stream large files to disk with express fileupload in Nodejs using useTempFiles and set limits to avoid memory issues. Optimize your large file uploads now.

- Repository: [expressjs/express](https://github.com/expressjs/express)
- Tags: how-to-guide
- Published: 2026-02-20

---

**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`](https://github.com/expressjs/express/blob/main/lib/express.js), the framework exports `json`, `raw`, `text`, and `urlencoded` parsers, but omits any multipart or file handling capabilities:

```javascript
// 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:

```bash
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.

```javascript
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 to `tempFileDir` instead 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:

```javascript
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:

```javascript
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:

```javascript
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`](https://github.com/expressjs/express/blob/main/lib/express.js)) exports only `json`, `raw`, `text`, and `urlencoded` parsers, requiring third-party middleware for file handling.
- **express-fileupload** handles large files safely when configured with `useTempFiles: true` and appropriate `tempFileDir` settings.
- Always set **size limits** via the `limits` option and enable `abortOnLimit` to prevent denial-of-service attacks.
- Use **streaming approaches** (either via `express-fileupload` temp files or direct `busboy` piping) 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`](https://github.com/expressjs/express/blob/main/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.