Multer

raw JSON →
1.1.1 verified Thu Apr 23 auth: no javascript

Multer is a Node.js middleware specifically designed for handling `multipart/form-data`, primarily used for processing file uploads in web applications built with the Express.js framework. It efficiently parses incoming request bodies and populates `req.body` with text fields and `req.file` or `req.files` with uploaded files, depending on the configured upload strategy. Built on top of `busboy`, Multer focuses exclusively on `multipart/form-data` and will not process other form encodings. The current stable version is 2.1.1, released on March 4, 2026, with a consistent release cadence that includes frequent security patches. It is a widely adopted library within the Express ecosystem for file upload functionalities.

error MulterError: Unexpected field
cause The field name provided in the HTML form's `<input type="file" name="...">` does not match the field name configured in Multer's middleware (e.g., `upload.single('avatar')` expects `name="avatar"`).
fix
Ensure the name attribute of your file input in the HTML form exactly matches the string passed to Multer methods like upload.single(), upload.array(), or the name property in upload.fields().
error req.file or req.files is undefined (no file uploaded)
cause This typically occurs when the HTML form is missing `enctype="multipart/form-data"`, the `destination` directory for disk storage is not writable or does not exist, or the Multer middleware is not correctly applied to the route.
fix
Verify that your HTML form has enctype="multipart/form-data". Check that the destination path for multer.diskStorage exists and is writable by the Node.js process. Ensure Multer middleware (e.g., upload.single()) is correctly placed before your route handler.
error Error: EBUSY: resource busy or locked, open '...' (for disk storage)
cause This can happen if the destination directory for file uploads is being accessed or locked by another process, or if the Multer stream is not properly drained/closed in older versions or specific error handling scenarios.
fix
Ensure the destination directory is accessible and not locked. For robust error handling, especially in earlier Multer versions, ensure all streams are properly handled or closed. Upgrading to the latest Multer version (v2.1.1+) includes fixes for error/abort handling that might mitigate some stream-related issues.
error Error: Multipart: Boundary not found
cause This error typically indicates that the incoming request is not a valid `multipart/form-data` request, often due to incorrect client-side configuration, corrupted data, or a proxy/load balancer modifying the request.
fix
Verify that your client-side code (HTML form or API request) correctly sets Content-Type: multipart/form-data and constructs the body according to the multipart specification. If using proxies, ensure they are not interfering with the request body.
breaking Multer v2.0.0 introduced a breaking change by raising the minimum supported Node.js version to 10.16.0. Users on older Node.js runtimes must upgrade their Node.js environment or remain on Multer v1.x.
fix Upgrade Node.js to version 10.16.0 or higher. For Node.js versions below 10.16.0, use Multer v1.x.
security Multer v2.1.1 fixes CVE-2026-3520 (GHSA-5528-5vmv-3xc2), a high-severity Denial of Service (DoS) vulnerability due to uncontrolled recursion. This could cause stack overflow by sending malformed requests.
fix Upgrade to Multer v2.1.1 or higher immediately.
security Multer v2.1.0 fixes CVE-2026-2359 (GHSA-v52c-386h-88mc) and CVE-2026-3304 (GHSA-xf7r-hgr6-v32p), both high-severity Denial of Service (DoS) vulnerabilities. These issues could lead to resource exhaustion via dropped connections during file upload or malformed requests and incomplete cleanup.
fix Upgrade to Multer v2.1.1 or higher (as 2.1.1 is the latest patch and includes all prior fixes).
security Multer v2.0.2 fixes CVE-2025-7338 (GHSA-fjgf-rc76-4x9p), a critical security vulnerability.
fix Upgrade to Multer v2.1.1 or higher.
security Multer v2.0.1 fixes CVE-2025-48997 (GHSA-g5hg-p3ph-g8qg), addressing an important security vulnerability.
fix Upgrade to Multer v2.1.1 or higher.
security Multer v2.0.0 fixes CVE-2025-47935 (GHSA-44fp-w29j-9vj5) and CVE-2025-47944 (GHSA-4pg4-qvpc-4q3h), which are critical security vulnerabilities.
fix Upgrade to Multer v2.1.1 or higher.
gotcha Multer explicitly only processes `multipart/form-data` forms. If your HTML form omits `enctype="multipart/form-data"`, Multer will not parse the request body, and `req.body`, `req.file`, or `req.files` will be empty or undefined.
fix Always ensure your HTML form includes the attribute `enctype="multipart/form-data"` for Multer to correctly process the upload.
npm install multer2
yarn add multer2
pnpm add multer2

Demonstrates basic file upload handling for single files, multiple files (up to 5), and text-only multipart forms using Multer middleware with Express.js, configuring disk storage with dynamic filenames.

import express from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import fs from 'fs';

// ESM equivalent of __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const app = express();
const port = 3000;
const uploadDir = path.join(__dirname, 'uploads');

// Ensure the uploads directory exists
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true });
}

// Configure disk storage
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, uploadDir);
  },
  filename: function (req, file, cb) {
    cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
  }
});

const upload = multer({ storage: storage });

// Serve a basic HTML form for upload
app.get('/', (req, res) => {
  res.send(`
    <!DOCTYPE html>
    <html>
    <head><title>Multer Upload</title></head>
    <body>
      <h2>Upload a Single File</h2>
      <form action="/profile-upload" method="post" enctype="multipart/form-data">
        <input type="file" name="avatar" />
        <button type="submit">Upload Avatar</button>
      </form>
      <hr>
      <h2>Upload Multiple Files (max 5)</h2>
      <form action="/gallery-upload" method="post" enctype="multipart/form-data">
        <input type="file" name="photos" multiple />
        <button type="submit">Upload Gallery</button>
      </form>
      <hr>
      <h2>Text-Only Form</h2>
      <form action="/text-data" method="post" enctype="multipart/form-data">
        <input type="text" name="username" placeholder="Username" />
        <button type="submit">Submit Text</button>
      </form>
    </body>
    </html>
  `);
});

// Handle single file upload
app.post('/profile-upload', upload.single('avatar'), (req, res) => {
  if (req.file) {
    console.log('Uploaded avatar:', req.file);
    res.send(`File uploaded successfully: ${req.file.originalname} saved to ${req.file.path}`);
  } else {
    res.status(400).send('No file uploaded.');
  }
});

// Handle multiple file upload
app.post('/gallery-upload', upload.array('photos', 5), (req, res) => {
  if (req.files && req.files.length > 0) {
    console.log('Uploaded photos:', req.files);
    res.send(`${req.files.length} files uploaded successfully.`);
  } else {
    res.status(400).send('No files uploaded.');
  }
});

// Handle text-only multipart form
app.post('/text-data', upload.none(), (req, res) => {
  console.log('Received text data:', req.body);
  res.send(`Text data received: ${JSON.stringify(req.body)}`);
});

app.listen(port, () => {
  console.log(`Server listening at http://localhost:${port}`);
  console.log(`Uploads will be saved to: ${uploadDir}`);
});