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:
npm install multer
Basic File Upload
Set up Multer to handle a single file:
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 theuploads/folderupload.single('file')handles one file from a form field named "file"req.filecontains file information
A user posts a file to /upload, and Multer saves it to uploads/.
Accessing File Information
After upload, req.file contains:
{
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:
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():
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:
upload.array('files', 5) // Max 5 files
Customizing File Names
By default, Multer generates random filenames. Configure storage to control this:
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:
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:
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:
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:
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):
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
// 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
// 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 multipleConfigure 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.



