Skip to main content
Node.js Basics
CHAPTER 25 Beginner

File Uploads with Multer

Updated: May 13, 2026
25 min read

# File Uploads with Multer

Welcome to Chapter 25! Until now, we have handled text data via express.json() and express.urlencoded(). But what happens when a user wants to upload a profile picture or a PDF document?

Standard JSON and URL-encoded formats cannot handle large binary files. To send files over the internet, browsers use a format called multipart/form-data. Express has no built-in way to read this format. To solve this, the Node.js community created a powerful middleware package named Multer.

---

1. Introduction

When a user submits an HTML form containing <input type="file">, the browser chops the file into multiple parts (hence "multipart") and streams it to your server.

Multer is an NPM package designed exclusively to handle multipart/form-data. It intercepts the incoming file stream, reconstructs the file, saves it to a designated folder on your hard drive, and attaches a new object (req.file) to the request so your route handler can access the file's details.

---

2. Learning Objectives

By the end of this chapter, you will be able to:

  • Install and configure the multer package.
  • Write HTML forms configured for multipart/form-data.
  • Create a Multer storage engine to specify where files should be saved.
  • Rename uploaded files dynamically to prevent overwriting.
  • Filter uploads to only accept specific file types (e.g., Images only).
  • Access file metadata using req.file.

---

3. Beginner-Friendly Explanations

The enctype Attribute

If you create an HTML form with a file input, but forget to add enctype="multipart/form-data" to the <form> tag, the browser will literally just send the *name* of the file (e.g., "vacation.jpg") as text, but it will not send the actual image data!

How Multer fits in

Multer acts as route-specific middleware. You don't usually apply it globally using app.use(), because not every route expects a file. You inject it directly into the specific POST route where the upload happens, just like the auth middleware from Chapter 24.

---

4. Syntax Explanation

Let's look at the basic setup to accept a single file upload.

```javascript id="ch25-syntax-1" const express = require('express'); const multer = require('multer'); const app = express();

// 1. Tell Multer to save files in the 'uploads/' directory const upload = multer({ dest: 'uploads/' });

// 2. Inject the middleware into the route. // 'avatar' must match the name="" attribute in the HTML input. app.post('/profile', upload.single('avatar'), (req, res) => { // 3. If successful, Multer attaches the file data to req.file console.log(req.file); res.send("File uploaded successfully!"); });

app.listen(3000);

1234567891011121314151617181920212223
**Output Explanation:**
If a user uploads an image, Multer intercepts it, saves it inside an `uploads` folder in your project, and then calls `next()`. The route handler runs, and `req.file` will contain an object with the file's original name, size, and the new path where it was saved.

---

## 5. Real-world Examples

**Social Media Apps:**
When you post a photo on Instagram:
1. Your phone sends a `multipart` request containing the image and the caption.
2. Instagram's Node.js backend uses Multer (or similar) to grab the image and save it to an Amazon S3 cloud bucket.
3. It grabs the caption from `req.body.caption`.
4. It saves the URL of the image and the caption to MongoDB.

---

## 6. Multiple Code Examples

Basic Multer setup works, but it strips the file extension (saving the file as `8b3a4f...` instead of `image.jpg`). To fix this, we must configure a **Storage Engine**.

### Example 1: Multer Storage Engine
This allows us to control the exact folder destination and the filename.

javascript id="ch25-code-1" const multer = require('multer'); const path = require('path');

// Configure Storage const storage = multer.diskStorage({ // Where to save the file destination: (req, file, cb) => { cb(null, 'public/images/'); // Save in public/images }, // What to name the file filename: (req, file, cb) => { // Create a unique name: timestamp + original extension const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); const ext = path.extname(file.originalname); cb(null, file.fieldname + '-' + uniqueSuffix + ext); // Example: avatar-1634567890-12345.jpg } });

// Initialize Multer with our storage config const upload = multer({ storage: storage });

123
### Example 2: Filtering File Types
Hackers might try to upload an `.exe` virus instead of a profile picture. We must write a **File Filter** to reject dangerous files.

javascript id="ch25-code-2" const fileFilter = (req, file, cb) => { // Check if the file's mimetype starts with 'image/' if (file.mimetype.startsWith('image/')) { cb(null, true); // Accept the file } else { cb(new Error('Invalid file type! Only images are allowed.'), false); // Reject } };

const upload = multer({ storage: storage, fileFilter: fileFilter, limits: { fileSize: 5 * 1024 * 1024 } // 5MB limit });

123
### Example 3: Multiple Files
If a user is uploading an album of photos, use `.array()` instead of `.single()`.

javascript id="ch25-code-3" // Expect an array of files from the input named 'gallery' (max 5 files) app.post('/album', upload.array('gallery', 5), (req, res) => { // Note: It is req.files (plural) for arrays! console.log(req.files.length + " files uploaded."); res.send("Album saved."); });

12345
---

## 7. Output Explanations

What does the `req.file` object look like?

json { "fieldname": "avatar", "originalname": "my_vacation.jpg", "encoding": "7bit", "mimetype": "image/jpeg", "destination": "public/images/", "filename": "avatar-1638293829-3829.jpg", "path": "public\\images\\avatar-1638293829-3829.jpg", "size": 150342 }

123456789101112131415161718192021222324252627282930313233343536373839
You take the `filename` or `path` from this object and save it to MongoDB as a string!

---

## 8. Common Mistakes

1. **Forgetting `enctype="multipart/form-data"`:** The HTML form will submit, but `req.file` will be undefined and the file will not be saved.
2. **Mismatched Field Names:** If HTML is `<input name="profilePic">`, but the route says `upload.single('avatar')`, Multer will throw an `Unexpected field` error. The names MUST match perfectly.
3. **Missing Folders:** If you configure Multer to save to `public/images/`, but that folder doesn't exist on your hard drive, Multer might crash. Create the folder manually first!

---

## 9. Best Practices

- **Never trust original filenames:** If two users upload `profile.jpg`, the second one will overwrite the first. Always generate a unique filename (using `Date.now()` or the `uuid` package).
- **Serve uploads statically:** Save user uploads inside the `public` folder so that `express.static` can easily serve them back to the frontend.
- **Enforce Limits:** Always set a `limits: { fileSize }` property. Otherwise, a malicious user could upload a 50GB file and instantly crash your server by filling up the hard drive.

---

## 10. Exercises

1. Install `multer`. Create an Express server.
2. Set up basic Multer with `dest: 'uploads/'`.
3. Create an HTML file with a form (`enctype="multipart/form-data"`). Include a text input for 'username' and a file input for 'document'.
4. Submit the form to the server and `console.log(req.file)` and `console.log(req.body.username)`.

---

## 11. Mini Project: Profile image uploader

**Objective:** Build an endpoint that receives a profile picture, validates it's an image, saves it uniquely, and returns the public URL.

**Step 1:** Setup
`npm init -y`
`npm install express multer ejs`
Create folders: `views/` and `public/uploads/`

**Step 2:** Form (`views/upload.ejs`)

html id="ch25-mini-project-1" <!DOCTYPE html> <html><body> <h2>Upload Profile Picture</h2> <!-- CRITICAL: enctype --> <form action="/upload" method="POST" enctype="multipart/form-data"> <input type="file" name="profilePic" required> <button type="submit">Upload</button> </form> </body></html>

1
**Step 3:** The Server (`app.js`)

javascript id="ch25-mini-project-2" const express = require('express'); const multer = require('multer'); const path = require('path');

const app = express(); app.set('view engine', 'ejs'); // Serve the public folder so we can view the uploaded images app.use(express.static('public'));

// Configure Multer Storage const storage = multer.diskStorage({ destination: (req, file, cb) => cb(null, 'public/uploads/'), filename: (req, file, cb) => { const uniqueName = Date.now() + path.extname(file.originalname); cb(null, file.fieldname + '-' + uniqueName); } });

// Configure Multer Filter & Limits const upload = multer({ storage: storage, limits: { fileSize: 2 * 1024 * 1024 }, // 2MB Limit fileFilter: (req, file, cb) => { if (file.mimetype.startsWith('image/')) cb(null, true); else cb(new Error('Only images allowed!')); } });

app.get('/', (req, res) => res.render('upload'));

// Upload Route (Inject the middleware!) app.post('/upload', upload.single('profilePic'), (req, res) => { try { if (!req.file) return res.status(400).send("No file uploaded."); // The image is now saved in public/uploads. // We construct a URL to send back to the user. const imageUrl = /uploads/${req.file.filename}; res.send( <h3>Upload Successful!</h3> <img src="${imageUrl}" width="200" style="border-radius:50%;"> <br><a href="/">Upload another</a> ); } catch (err) { res.status(400).send(err.message); } });

app.listen(3000, () => console.log('Server running')); ``

---

12. Coding Challenges

Challenge 1: Modify the mini-project form to include an <input type="text" name="caption">. Update the backend to extract req.body.caption and display it below the uploaded image on the success page. (Notice how Multer automatically parses the text fields alongside the file!).

Challenge 2: Write an Express global error handler that specifically catches Multer's "File too large" error and sends a friendly message to the user instead of crashing the server.

---

13. MCQs with Answers

Q1: Which HTML attribute is strictly required for a form to upload files? A) method="FILE" B) enctype="multipart/form-data" C) action="/upload" D) upload="true" Answer: B

Q2: What is Multer? A) A database system. B) A templating engine. C) Express middleware for handling multipart/form-data. D) A tool for hashing passwords. Answer: C

Q3: Which Multer configuration allows you to define exactly where a file is saved and what its name will be? A) multer.diskStorage() B) multer.config() C) multer.settings() D) multer.setup() Answer: A

Q4: If a user uploads a file using upload.single('avatar'), where does Express store the file's metadata? A) req.body.avatar B) req.data C) req.upload D) req.file Answer: D

---

14. Interview Questions

  1. 1. Why can't express.json() handle file uploads?
*Answer:* express.json() is designed to parse text-based JSON payloads. File uploads consist of massive streams of binary data (images, videos) encoded in multipart/form-data. JSON parsers cannot read or process these binary streams. Multer is required to buffer and write this binary data to the disk.
  1. 2. Why is it dangerous to trust file.originalname when saving files?
*Answer:* Two users might upload a file named image.png. The second upload will overwrite the first user's image on the server. Additionally, hackers might try to upload files with malicious names (like ../../../script.js) to attempt a directory traversal attack. Generating unique, random filenames on the backend mitigates both risks.

---

15. FAQs

Q: Does Multer save the image to MongoDB? A: No! MongoDB is for text and JSON data. Saving massive binary files directly into a database is extremely inefficient and will bloat the database immediately. You save the physical file to your server's hard drive (via Multer), and you save the URL string (e.g., /images/pic1.jpg) into MongoDB.

Q: How do massive companies handle uploads? A: Large companies (like Netflix) don't save files on the same server that runs Node.js. They use Multer to process the file in memory (multer.memoryStorage()) and instantly stream it to a cloud storage service like Amazon S3, Azure Blob, or Cloudinary.

---

16. Summary

  • File uploads require the multipart/form-data encoding type.
  • Multer is the standard Express middleware for handling multipart data.
  • Use multer.diskStorage() to control filenames and folder destinations.
  • Always generate unique filenames to prevent overwriting.
  • Always use a fileFilter and size limits for security.
  • Data is accessed via req.file (or req.files` for arrays).

---

17. Next Chapter Recommendation

We've covered databases, authentication, and file uploads. Our apps are getting complex! With complexity comes bugs. If something breaks in production, how do you fix it without crashing the server? In Chapter 26: Error Handling and Debugging, we will learn how to write a Global Error Handler and use modern debugging techniques.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·