I want to use multer in my nodejs app to upload user profile pictures. My routes are managed by express router. I have checked a lot of tutorials but nothing matches my exact use case. I want to let the users upload their profile pictures to my API, but before the request reaches the upload function I want to perform some validations like password and API key checks. here is my upload controller,
const multer = require("multer");
const path = require("path");
const dp_storage = multer.diskStorage({
destination: path.join(__dirname, "../user_uploads/images/dp"),
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
// Init dp Upload
const dp_upload = multer({
storage: dp_storage,
limits: { fileSize: 2000000 }, // 1 mb
fileFilter: function (req, file, cb) {
checkFileTypeForUserDP(file, cb);
},
}).single("dp");
function checkFileTypeForUserDP(file, cb) {
// Allowed ext
let filetypes = /jpeg|jpg|png|gif|webp/;
// Check ext
let extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
let mimetype = filetypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb("Error: jpeg, jpg, png, gif Images Only!");
}
}
exports.uploadDP = async (req, res) => {
try {
dp_upload(req, res, (err) => {
if (err) {
console.log(err);
} else {
if (req.file == undefined) {
res.status(404).json({
success: false,
msg: "File is undefined!",
file: `uploads/${req.file.filename}`,
});
} else {
res.status(200).json({
success: true,
msg: "File Uploaded!",
file: `uploads/${req.file.filename}`,
});
}
}
});
} catch (error) {console.log(error);}
};
The above code works fine if I use it directly without any API key validation or user authentication.
Here is my router,
const express = require("express");
const router = express.Router();
const { authenticateUser ,apiKeyCheck} = require("../server");
const { uploadDP } = require("../controllers/file");
//this route works
router.post(
"/upload/dp_without_authentication",
uploadDP
);
//this is not working
router.post(
"/upload/dp",
apiKeyCheck,
authenticateUser,
uploadDP
);
module.exports = router;
The "/upload/dp"
route is failing because the apiKeyCheck and authenticateUser
functions can not read the user credentials from req.body
.
So, in order to fix that I have added the following lines to my main server file,
const multer = require("multer");
const upload = multer();
app.use(upload.array());
But now the uploadDP
function is not even called, instead it returns the following error:
MulterError: Unexpected field
at wrappedFileFilter (/Users/sujith/Documents/Personal_projects/VocabularyServer/node_modules/multer/index.js:40:19)
at Busboy.<anonymous> (/Users/sujith/Documents/Personal_projects/VocabularyServer/node_modules/multer/lib/make-middleware.js:115:7)
at Busboy.emit (node:events:394:28)
at Busboy.emit (/Users/sujith/Documents/Personal_projects/VocabularyServer/node_modules/busboy/lib/main.js:38:33)
at PartStream.<anonymous> (/Users/sujith/Documents/Personal_projects/VocabularyServer/node_modules/busboy/lib/types/multipart.js:213:13)
at PartStream.emit (node:events:394:28)
at HeaderParser.<anonymous> (/Users/sujith/Documents/Personal_projects/VocabularyServer/node_modules/busboy/node_modules/dicer/lib/Dicer.js:51:16)
at HeaderParser.emit (node:events:394:28)
at HeaderParser._finish (/Users/sujith/Documents/Personal_projects/VocabularyServer/node_modules/busboy/node_modules/dicer/lib/HeaderParser.js:68:8)
at SBMH.<anonymous> (/Users/sujith/Documents/Personal_projects/VocabularyServer/node_modules/busboy/node_modules/dicer/lib/HeaderParser.js:40:12)
If I remove the file from postman request, it is able to call uploadDP
function.
What am I doing wrong here?
As Multer official docs warns the use of multer as a global middleware;
WARNING: Make sure that you always handle the files that a user uploads. Never add multer as a global middleware since a malicious user could upload files to a route that you didn't anticipate. Only use this function on routes where you are handling the uploaded files.
Therefore I recommend exploring a safer way of handling files as answered here