Multer File Upload in Express.js: Complete Guide
Multer is a Node.js middleware for handling multipart/form-data, primarily used for file uploads in Express.js applications. In this guide, we'll learn how to implement file uploads for product images and galleries in an inventory management system.
Multer is a Node.js middleware for handling multipart/form-data, primarily used for file uploads in Express.js applications. In this guide, we'll learn how to implement file uploads for product images and galleries in an inventory management system.
Installation
npm install multerMulter Configuration
Setting up Multer with disk storage:
const multer = require("multer");
const path = require("path");
const fs = require("fs");
// Create uploads directory if it doesn't exist
const uploadDir = "uploads/";
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, uploadDir);
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 10000);
const uploadedFileName =
file.fieldname + "-" + uniqueSuffix + "." + file.mimetype.split("/")[1];
cb(null, uploadedFileName);
},
});
const upload = multer({
storage: storage,
limits: {
fileSize: 5 * 1024 * 1024, // 5MB limit
},
fileFilter: (req, file, cb) => {
// Only allow images
if (file.mimetype.startsWith("image/")) {
cb(null, true);
} else {
cb(new Error("Only image files are allowed!"), false);
}
},
});Single File Upload
const uploadSingleFile = upload.single("file");
function storeSingleFile(req, res, next) {
return new Promise((resolve, reject) => {
uploadSingleFile(req, res, async function (err) {
if (err instanceof multer.MulterError) {
console.log("Multer error:", err);
reject(err);
} else if (err) {
console.log("Upload error:", err);
reject(err);
}
resolve({
message: "File uploaded successfully",
body: req.body,
file: req.file ? req.file : null,
});
});
});
}
// Usage in route
router.post("/upload", async (req, res, next) => {
try {
await storeSingleFile(req, res, next);
res.json({
success: true,
message: "File uploaded",
file: req.file,
});
} catch (error) {
res.status(400).json({
success: false,
message: error.message,
});
}
});Multiple File Upload
Handling product images with main image and gallery:
// For products: main image (required) + gallery images (optional)
const uploadProductFiles = upload.fields([
{ name: "product_image", maxCount: 1 }, // Main product image (required)
{ name: "product_gallery", maxCount: 10 }, // Gallery images (optional, max 10)
]);
function storeProductFiles(req, res, next) {
return new Promise((resolve, reject) => {
uploadProductFiles(req, res, async function (err) {
if (err instanceof multer.MulterError) {
console.log("Multer error for product files:", err);
reject(err);
} else if (err) {
console.log("Upload error for product files:", err);
reject(err);
}
resolve({
message: "Files uploaded successfully",
body: req.body,
files: req.files ? req.files : null,
});
});
});
}
// Usage in route
router.post("/products", async (req, res, next) => {
try {
await storeProductFiles(req, res, next);
// Access files
const mainImage = req.files?.product_image?.[0];
const galleryImages = req.files?.product_gallery || [];
// Process files...
res.json({
success: true,
message: "Product created",
files: {
mainImage: mainImage?.filename,
gallery: galleryImages.map(f => f.filename),
},
});
} catch (error) {
res.status(400).json({
success: false,
message: error.message,
});
}
});File Validation
Adding custom validation:
const upload = multer({
storage: storage,
limits: {
fileSize: 5 * 1024 * 1024, // 5MB
files: 10, // Max 10 files
},
fileFilter: (req, file, cb) => {
// Check file type
const allowedTypes = ["image/jpeg", "image/jpg", "image/png", "image/webp"];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error("Invalid file type. Only JPEG, PNG, and WebP are allowed!"), false);
}
},
});Error Handling
router.post("/upload", async (req, res) => {
try {
await storeProductFiles(req, res);
// Check if main image is required
if (!req.files?.product_image || req.files.product_image.length === 0) {
return res.status(400).json({
success: false,
message: "Product image is required",
});
}
// Process files...
} catch (error) {
if (error instanceof multer.MulterError) {
if (error.code === "LIMIT_FILE_SIZE") {
return res.status(400).json({
success: false,
message: "File size too large. Maximum size is 5MB",
});
}
if (error.code === "LIMIT_FILE_COUNT") {
return res.status(400).json({
success: false,
message: "Too many files. Maximum is 10 files",
});
}
}
return res.status(400).json({
success: false,
message: error.message || "File upload failed",
});
}
});Best Practices
- Always validate file types and sizes
- Use unique filenames to prevent conflicts
- Set appropriate file size limits
- Handle errors gracefully
- Create uploads directory if it doesn't exist
- Clean up old files periodically
- Consider using cloud storage for production
Conclusion
Multer provides a straightforward solution for handling file uploads in Express.js applications. With proper configuration, validation, and error handling, you can create robust file upload functionality for product images, galleries, and other file types in inventory management systems.