File Uploads with Multer

Multer is Express middleware for multipart/form-data that parses incoming file uploads, saving each file to disk or memory and exposing it on req.file or req.files so your route can store and validate it.

Learn File Uploads with Multer in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll learn why uploads need a special parser, how to configure disk vs memory storage, how single, array, and fields differ, and how to lock things down with a fileFilter and size limits — the standard way Express apps accept avatars, photos, and documents.

What You'll Learn in This Lesson

1️⃣ Wiring Up Multer

A browser sends an uploaded file as multipart/form-data , not JSON, so express.json() can't touch it. Multer is the middleware that understands that format. You configure a storage engine — here diskStorage with a destination folder and a filename function — and then add upload.single('avatar') to the route.

After Multer runs, the file is on req.file (with filename , originalname , size , mimetype ) and any plain text fields are on req.body . The filename function is where you control how files are named on disk — crucial for avoiding collisions.

2️⃣ Naming, Filtering & Limits — the Logic

The trickiest parts of uploads aren't the server plumbing — they're the decisions : what to name a file so two uploads don't collide, which types to accept, and how big is too big. The block below runs as plain Node and demonstrates exactly the logic Multer applies for you: a unique filename, a mime-type filter, and a size cap.

3️⃣ Disk vs Memory, and Many Files

Multer ships two storage engines. diskStorage streams the file to a folder (best for keeping files on the server). memoryStorage holds it in RAM as a Buffer on req.file.buffer (best when you forward it straight to cloud storage). The example below uses memory storage and shows the real metadata Multer attaches after a small upload:

For more than one file, swap the helper: upload.array('photos', 5) for many files under one field, or upload.fields([...]) for several named fields. And the two safety knobs — fileFilter and limits — are configured right on the Multer instance:

Your turn. Fill in the two ___ blanks so the function builds a clean stored name, then run it.

No blanks this time — just a brief and an outline. Write the function yourself, run it, and check your output against the example. This is the same decision Multer's fileFilter and limits make.

📋 Quick Reference — Multer

Practice quiz

Why can't express.json() parse a file upload?

  • Files are too large for it
  • Uploads are sent as multipart/form-data, a different encoding it doesn't understand
  • It only runs on GET requests
  • It needs a database first

Answer: Uploads are sent as multipart/form-data, a different encoding it doesn't understand. express.json() only parses application/json; uploads arrive as multipart/form-data, which needs Multer.

After upload.single('avatar') runs, where is the uploaded file?

  • req.body.avatar
  • req.params.file
  • req.file
  • req.upload

Answer: req.file. upload.single puts the single file on req.file and any text fields on req.body.

Which storage engine keeps the file in RAM as a Buffer on req.file.buffer?

  • multer.memoryStorage()
  • multer.diskStorage()
  • multer.bufferStorage()
  • multer.ramStorage()

Answer: multer.memoryStorage(). memoryStorage holds the file in RAM as a Buffer, ideal when forwarding it straight to cloud storage.

Which helper accepts up to five files all sharing one field name?

  • upload.single('photos')
  • upload.fields('photos', 5)
  • upload.many('photos')
  • upload.array('photos', 5)

Answer: upload.array('photos', 5). upload.array('photos', 5) accepts many files under one field name and puts them on req.files.

What does the limits.fileSize option do?

  • Sets the upload folder
  • Caps the bytes per file, aborting with a MulterError if exceeded
  • Renames the file
  • Counts the number of fields

Answer: Caps the bytes per file, aborting with a MulterError if exceeded. limits.fileSize caps each file's size; Multer aborts with a MulterError when a file is too large.

Inside a fileFilter, how do you reject a file and fail the whole request?

  • return false
  • cb(null, true)
  • cb(new Error('not allowed'), false)
  • throw 'no'

Answer: cb(new Error('not allowed'), false). Pass an Error to cb to fail the request; cb(null, false) skips the file quietly.

Why should you never write the user's original filename straight to disk?

  • It is always too long
  • Multer forbids it
  • It is slower than a random name
  • A user could supply a path like ../../etc/passwd, so generate your own name and borrow only the extension

Answer: A user could supply a path like ../../etc/passwd, so generate your own name and borrow only the extension. The original name is untrusted; generate a safe unique name and keep only the extension.

req.file is undefined after an upload. What is a likely cause?

  • The form field name doesn't match the name in upload.single(), or the form lacks multipart/form-data
  • The file was too small
  • memoryStorage was used
  • The server is on the wrong port

Answer: The form field name doesn't match the name in upload.single(), or the form lacks multipart/form-data. The form field name must match upload.single's name and the form must use enctype multipart/form-data.

Why is trusting file.mimetype alone not enough for security?

  • It is always empty
  • It is supplied by the client and can be spoofed
  • It is only set for images
  • Multer deletes it

Answer: It is supplied by the client and can be spoofed. mimetype comes from the client and can be faked, so also validate real contents server-side.

Which upload helper accepts files from several differently-named fields at once?

  • avatar
  • docs

Answer: avatar. upload.fields([...]) accepts several named fields and keys them on req.files by field name.