# Handling File Uploads in Express with Multer

Users need to upload files: profile pictures, documents, resumes, attachments. Handling this in Express requires special handling. A library called Multer makes it straightforward.

## Why File Uploads Need Special Handling

Regular form data is text. But files are binary. When uploading files, the request format changes to `multipart/form-data`, which handles both text fields and binary files.

Express doesn't automatically parse this format. That's where Multer comes in.

## What is Multer?

Multer is middleware for Express that handles file uploads. It parses multipart form data, extracts files, and saves them to disk or memory.

Install it:

```bash
npm install multer
```

## Basic File Upload

Set up Multer to handle a single file:

```javascript
import express from 'express';
import multer from 'multer';

const app = express();
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('file'), (req, res) => {
  if (!req.file) {
    return res.status(400).send('No file uploaded');
  }
  res.send(`File ${req.file.filename} uploaded`);
});

app.listen(3000);
```

*   `multer({ dest: 'uploads/' })` saves files to the `uploads/` folder
    
*   `upload.single('file')` handles one file from a form field named "file"
    
*   `req.file` contains file information
    

A user posts a file to `/upload`, and Multer saves it to `uploads/`.

## Accessing File Information

After upload, `req.file` contains:

```javascript
{
  fieldname: 'file',
  originalname: 'photo.jpg',
  encoding: '7bit',
  mimetype: 'image/jpeg',
  destination: 'uploads/',
  filename: 'file-1234567890',
  path: 'uploads/file-1234567890',
  size: 154321
}
```

Use this information to track what was uploaded:

```javascript
app.post('/upload', upload.single('file'), (req, res) => {
  if (!req.file) {
    return res.status(400).send('No file uploaded');
  }

  res.json({
    message: 'File uploaded successfully',
    filename: req.file.filename,
    originalName: req.file.originalname,
    size: req.file.size,
    path: req.file.path
  });
});
```

## Multiple File Uploads

Handle multiple files with `upload.array()`:

```javascript
import express from 'express';
import multer from 'multer';

const app = express();
const upload = multer({ dest: 'uploads/' });

app.post('/upload-multiple', upload.array('files'), (req, res) => {
  if (!req.files || req.files.length === 0) {
    return res.status(400).send('No files uploaded');
  }

  const fileInfo = req.files.map(file => ({
    filename: file.filename,
    originalName: file.originalname,
    size: file.size
  }));

  res.json({ files: fileInfo });
});

app.listen(3000);
```

`req.files` is an array of uploaded files.

You can limit the number of files:

```javascript
upload.array('files', 5) // Max 5 files
```

## Customizing File Names

By default, Multer generates random filenames. Configure storage to control this:

```javascript
import express from 'express';
import multer from 'multer';
import path from 'path';

const app = express();

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    // Keep original extension
    const ext = path.extname(file.originalname);
    const name = path.basename(file.originalname, ext);
    const uniqueName = name + '-' + Date.now() + ext;
    cb(null, uniqueName);
  }
});

const upload = multer({ storage });

app.post('/upload', upload.single('file'), (req, res) => {
  res.json({ filename: req.file.filename });
});

app.listen(3000);
```

Now files are named: `photo-1234567890.jpg` instead of random names.

## Filtering File Types

Validate file types to prevent unwanted uploads:

```javascript
import express from 'express';
import multer from 'multer';

const app = express();

const storage = multer.diskStorage({
  destination: 'uploads/',
  filename: (req, file, cb) => {
    cb(null, Date.now() + '-' + file.originalname);
  }
});

const fileFilter = (req, file, cb) => {
  // Allow only images
  const allowedMimes = ['image/jpeg', 'image/png', 'image/gif'];
  
  if (allowedMimes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error('Only image files are allowed'));
  }
};

const upload = multer({ storage, fileFilter });

app.post('/upload-image', upload.single('image'), (req, res) => {
  res.json({ message: 'Image uploaded', filename: req.file.filename });
});

app.listen(3000);
```

The `fileFilter` function checks the MIME type and rejects non-image files.

## Limiting File Size

Prevent huge files from being uploaded:

```javascript
const upload = multer({
  dest: 'uploads/',
  limits: {
    fileSize: 5 * 1024 * 1024 // 5MB
  }
});

app.post('/upload', upload.single('file'), (req, res) => {
  res.json({ message: 'File uploaded' });
});
```

Files larger than 5MB are rejected.

## Handling Multiple File Fields

Upload different types of files in one request:

```javascript
const app = express();

const upload = multer({ dest: 'uploads/' });

const fileUpload = upload.fields([
  { name: 'profilePic', maxCount: 1 },
  { name: 'coverPhoto', maxCount: 1 },
  { name: 'documents', maxCount: 5 }
]);

app.post('/upload-profile', fileUpload, (req, res) => {
  const profilePic = req.files.profilePic?.[0];
  const coverPhoto = req.files.coverPhoto?.[0];
  const documents = req.files.documents;

  res.json({
    profilePic: profilePic?.filename,
    coverPhoto: coverPhoto?.filename,
    documentCount: documents?.length || 0
  });
});

app.listen(3000);
```

`req.files` contains objects for each field name.

## Complete Realistic Example

Here's a complete user profile upload system:

```javascript
import express from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';

const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));

app.use(express.json());

// Configure storage
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    const uploadDir = 'uploads/profiles';
    cb(null, uploadDir);
  },
  filename: (req, file, cb) => {
    const ext = path.extname(file.originalname);
    const name = 'profile-' + Date.now() + ext;
    cb(null, name);
  }
});

// Filter to allow only images
const fileFilter = (req, file, cb) => {
  const allowedMimes = ['image/jpeg', 'image/png'];
  if (allowedMimes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error('Only JPEG and PNG images allowed'));
  }
};

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

// Serve uploaded files
app.use('/images', express.static('uploads/profiles'));

// Upload endpoint
app.post('/upload-profile', upload.single('profilePic'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }

  const imageUrl = `/images/${req.file.filename}`;

  res.json({
    message: 'Profile picture uploaded',
    filename: req.file.filename,
    url: imageUrl,
    size: req.file.size
  });
});

// Error handler for Multer
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    if (err.code === 'FILE_TOO_LARGE') {
      return res.status(400).json({ error: 'File too large' });
    }
    return res.status(400).json({ error: err.message });
  }
  if (err) {
    return res.status(400).json({ error: err.message });
  }
  next();
});

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

## Memory Storage

Store files in memory instead of disk (useful for processing before saving):

```javascript
const storage = multer.memoryStorage();
const upload = multer({ storage });

app.post('/process-file', upload.single('file'), (req, res) => {
  // File is in req.file.buffer (as a Buffer)
  const content = req.file.buffer.toString();
  res.json({ message: 'File processed' });
});
```

## Common Mistakes

**Mistake 1: Not handling errors**

```javascript
// Wrong - crashes on file too large
app.post('/upload', upload.single('file'), (req, res) => {
  res.send('Done');
});

// Right - add error handler
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    res.status(400).json({ error: err.message });
  } else {
    res.status(500).json({ error: 'Server error' });
  }
});
```

**Mistake 2: Trusting original filename**

```javascript
// Wrong - security risk
const filename = file.originalname;

// Right - generate safe filename
const filename = Date.now() + '-' + file.originalname;
```

**Mistake 3: Not validating file type**

Always validate using MIME type or file extension, preferably both.

## Key Takeaways

*   Multer is middleware for handling file uploads in Express
    
*   Use `upload.single()` for one file, `upload.array()` for multiple
    
*   Configure storage to control where and how files are saved
    
*   Use fileFilter to validate file types
    
*   Set size limits to prevent huge uploads
    
*   Always generate unique, safe filenames
    
*   Use error handlers to gracefully handle upload errors
    
*   Serve uploaded files as static files with `express.static`
    

File uploads are common in web applications. Master Multer, and you can confidently handle user-uploaded content.
