I'm using Multer as Express middleware. In my example I'm checking that the file extension and mimetype are correct (for a wave file), and I want to respond with 415 if they aren't. However I don't know how to do this with Multer's fileFilter, so I'm checking if the file exists in router handler's request object, which feels a bit awkward. Also maybe I want to implement different fileFilter's and error codes in the future. Is there a recommended pattern for setting response statuses with Multer in Express?
const upload = multer(
{
dest: UPLOAD_PATH,
fileFilter: function(req, file, cb) {
const filetypes = /wave|wav/;
const mimetype = filetypes.test(file.mimetype);
const extname = filetypes.test(
path.extname(file.originalname).toLowerCase());
cb(null, (mimetype && extname));
},
}
);
router.post('/', upload.single('wave'), (req, res) => {
const file = req.file;
if (!file) {
return res.status(415).send('Only audio/wav files are supported.');
}
// Do some async task with file
return res.sendStatus(200);
});
You can do it like this:
const upload = multer({
dest: 'uploads/',
fileFilter: function (req, file, cb) {
const filetypes = /wave|wav/;
const mimetype = filetypes.test(file.mimetype);
const extname = filetypes.test(
path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
cb(undefined, true);
} else {
req.notWAVEMimeType = 'Only audio/wav files are supported.';
return cb(undefined, false);
}
}
});
router.post('/', upload.single('wave'), (req, res) => {
if (req.notWAVEMimeType) {
return res.status(415).end(req.notWAVEMimeType);
}
// Do some async task with file
return res.sendStatus(200);
});
The notWAVEMimeType property on the request object allows you to return the error message to the POST method. According to this you can add other mime types and check the existence of the related property of req afterwards.
Related
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
I am trying to upload images in an express server using multer, however, uploading images using postman using the route below, gives the json message { msg: 'image uploaded successfully' } (i.e., the route is reached correctly), but req.file gives undefined. Why? the related file structure is as follows, to make sure I am referencing the destination correctly:
-backend
--routes
---uploadRoutes.js
--server.js
-frontend
-uploads
uploadRoutes.js
import path from 'path';
import express from 'express';
import multer from 'multer';
const router = express.Router();
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, 'uploads');
},
filename(req, file, cb) {
cb(
null,
`${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`
);
},
});
function checkFileType(file, cb) {
const filetypes = /jpg|jpeg|png/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = filetypes.test(file.mimetype);
if (extname && mimetype) {
return cb(null, true);
} else {
cb('Images only!');
}
}
const upload = multer({
storage,
fileFilter: function (req, file, cb) {
checkFileType(file, cb);
},
});
router.post('/', upload.single('image'), (req, res) => {
console.log(req.file);
try {
res.status(200).json({ msg: 'image uploaded successfully' });
} catch (error) {
console.error(error.message);
}
// res.send(`/${req.file.path}`);
});
export default router;
just check the header and body form-data request, because your code is correctly if you have this line in the app file
app.use("/uploads", express.static("uploads"));
header of request
Currently the image gets stored in my upload folder. Since i am new to node.js and multer can anyone guide me on how to store these images to google drive?
https://developers.google.com/drive/api/v3/quickstart/nodejs
When through these link but dont know how to implement it in my project. The code provided in this link uploades an image to google drive but the path of the file has to be harcoded. Can anyone please help me out. I am beginner in back end development.
here is my code for reference
```
const express = require("express");
const router = express.Router();
const multer = require("multer");
const path = require("path");
//set storage engine
const storage = multer.diskStorage({
destination: "./public/uploads/",
filename: function(req, file, cb) {
cb(null,file.fieldname + "-" + Date.now() + path.extname(file.originalname));
}
});
//init upload
const upload = multer({
storage: storage,
limits: { fileSize: 10000000 },
fileFilter: function(req, file, cb) {
checkFileType(file, cb);
}
}).single("myImage");
//check file type
function checkFileType(file, cb) {
//Allowed ext
const fileTypes = /jpeg|jpg|png|gif/;
//check ext
const extname = fileTypes.test(
path.extname(file.originalname).toLocaleLowerCase()
);
//check mime
const mimetype = fileTypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb("Error: Images Only!");
}
}
router.get("/", (req, res) => res.render("./index"));
router.post("/upload", (req, res) => {
upload(req, res, err => {
if (err) {
res.render("index", {
msg: err
});
} else {
// console.log(req.file);
// res.send("test");
if (req.file == undefined) {
res.render("index", {
msg: "Error: No File Selected!"
});
} else {
res.render("index", {
msg: "File uploaded!",
file: `uploads/${req.file.filename}`
});
}
}
});
});
module.exports = router;
```
const upload = multer({ dest: `${__dirname}/uploads/images` });
app.post(
"/api/users/:id/uploadProfilePic",
upload.single("image"),
updateProfilePic
);
const updateProfilePic = async (req, res) => {
const userId = req.param("id");
if (userId && isNumber(userId)) {
// When using the "single"
// data come in "req.file" regardless of the attribute "name". *
const tmpPath = req.file.path;
// The original name of the uploaded file
// stored in the variable "originalname". *
const targetPath = `uploads/images/${req.file.originalname}`;
/** A better way to copy the uploaded file. **/
const src = fs.createReadStream(tmpPath);
const dest = fs.createWriteStream(targetPath);
src.pipe(dest);
src.on("end", () => {
res.status(200).send("complete");
});
src.on("error", err => {
res.status(500).send(err);
});
}
};
In my express app, I have the following code for uploading an image - this seems to upload the image successfully, but it also creates this data blob in my uploads folder -
You can do something like
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
})
const upload = multer({storage: storage})
I'm using express-fileupload module to parse uploaded file.
Upload is done with axios.
const formData = new FormData();
formData.append("file", data.gameCover);
formData.append("gameTitle", data.gameTitle);
formData.append("gamePrice", data.gamePrice);
formData.append("description", data.description);
return axios.post(apiUrl + "/games/add", formData).then(res => {
dispatch({ type: ADD_GAME, payload: res.data.game });
});
This is the POST request
Serverside code looks like this:
router.use(fileUpload());
router.post("/add", (req, res) => {
if (!req.files) return res.status(400).send("No files were uploaded.");
Of course I'm getting "No files were uploaded" when trying to upload.
Finally after debugging step after step found that data.gameCover is an array
so that's the solution
formData.append("file", data.gameCover[0]);
You can optionaly use multer for handling multipart/formdata.
you can then get the uploaded file as follows
const express = require('express');
const path = require('path');
const router = express.Router();
const multer = require('multer');
const storage = multer.diskStorage(
{destination: (req, file, cb)=>{
cb(null, path.join(__dirname, '../uploads'));
},
filename: (req, file, cb)=>{
cb(null, file.originalname);
}
});
let upload = multer({storage: storage});
// access the uploaded file with your route handler
router.post('/upload',upload.single('file'), (req, res, next)=> {
if(req.file){
//you can access your uploaded file
}
});