Skip to content

Configure Multer

We want to intercept inbound file traffic explicitly, test uploaded files to ensure they are images, deposit them into our /public/uploads directory, and then save the file metadata (as a projectImage subdocument) in the appropriate project document.

We’ll use Multer’s diskStorage to handle the file system operations. The destination function will create a project-specific folder, and the filename function will generate a unique filename for the uploaded file.

We’ll also add a fileFilter function to ensure that only images are uploaded.

All of this logic is contained within the middleware/upload.js file.

middleware/upload.js
const fs = require("fs");
const path = require("path");
const multer = require("multer");
const storage = multer.diskStorage({
destination: (req, file, cb) => {
// 1. Grab the slug from the query string (or fallback)
const slug = req.query.slug || "uncategorized";
// 2. Define the project-specific path
const dir = `./public/uploads/${slug}`;
// 3. Create the directory if it doesn't exist yet
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
// 4. Tell Multer to save the file here
cb(null, dir);
},
filename: (req, file, cb) => {
const safeBase = path
.basename(file.originalname, path.extname(file.originalname))
.toLowerCase()
.replace(/[^a-z0-9\-]+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
const unique = Date.now();
cb(null, `${safeBase}-${unique}${path.extname(file.originalname)}`);
},
});
function fileFilter(req, file, cb) {
// Keep it simple: images only
if (!file.mimetype.startsWith("image/")) return cb(null, false);
cb(null, true);
}
const upload = multer({ storage, fileFilter });
module.exports = upload;
Neo-Retro isometric diagram showing Multer middleware filtering a file stream for images and organizing them into specific project folders.
Professor Solo

Notice the fileFilter checks for image/. We are silently rejecting non-images like .pdf or .exe files. Also, our filename logic strips out dangerous characters. Never blindly trust user input or file names!

By organizing our uploads into folders based on the project slug, we introduce coupling. If a project’s slug is ever edited later, the existing images remain stranded in the old folder! You’ll either need to write logic to rename the folder when a slug changes or lock down the slug field after creation.

With our middleware prepared, we can turn our attention to the administrative user interface to securely transmit the file.