How to validate file extension with Multer middleware - node.js

I use Multer for uploading file or images. Problem is that it is impossible to validate the real file extension.
Example: If someone rename filename.exe to filename.png, then it is still validate to upload.
Can you suggest me the solution to handle this issue? Thanks
I used like this but need verify real ext of files
fileFilter: async function (req, file, callback) {
var ext = path.extname(file.originalname);
if(ext !== '.png' && ext !== '.jpg' && ext !== '.gif' && ext !== '.jpeg' && ext !== '.zip') {
return callback(new Error('Only images and zip are allowed'));
}
// I want next function to validate real ext of files here.
callback(null, true);
},

Starting with Multer 2.x.x You can check for both the extension and the MIME type of the uploaded parameter. Here is a sample code.
const storage = multer.diskStorage({
destination: './uploadedContent',
filename: function(_req, file, cb){
cb(null,file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
var upload = multer({
storage: storage,
limits: {
fields: 5,
fieldNameSize: 50, // TODO: Check if this size is enough
fieldSize: 20000, //TODO: Check if this size is enough
// TODO: Change this line after compression
fileSize: 15000000, // 150 KB for a 1080x1080 JPG 90
},
fileFilter: function(_req, file, cb){
checkFileType(file, cb);
}
}).single('postPicture');
function checkFileType(file, cb){
// Allowed ext
const filetypes = /jpeg|jpg|png|gif/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: Images Only!');
}
}
Don't forget to check your file permissions too. You don't want the uploaded file to be executed somehow.
Sadly the latest npm release is 1.4.3 where the Multer mime type is based on the type property that comes from the client. That property at least in windows is dependent of the file extension, not on the file content.

There are two steps:
Filtering by mime type provided by multer on fileFilter options
Filtering the real mime type from the file buffer/stream using file-type.
Define whitelist mime type:
const whitelist = [
'image/png',
'image/jpeg',
'image/jpg',
'image/webp'
]
Example of the first step:
const upload = multer({
storage: multer.diskStorage({
destination: 'public/uploads/',
filename: (req, file, cb) => {
const name = slugify(file.originalname, { lower: true })
cb(null, `${new Date().getTime()}-${name}`)
},
}),
fileFilter: (req, file, cb) => {
if (!whitelist.includes(file.mimetype)) {
return cb(new Error('file is not allowed'))
}
cb(null, true)
}
})
Example of the second step:
const FileType = require('file-type')
// ....
// first step
// ....
/**
* Second step
*/
app.use('store', upload.single('image'), async (req, res, next) => {
const meta = await FileType.fromFile(req.file.path)
if (!whitelist.includes(meta.mime)) {
return next(new Error('file is not allowed'))
}
res.json({
file: req.file,
body: req.body,
})
})
I tried to rename a document.pdf -> document.png and it passed the first check but got caught on the second one.

Basically what you need is something which can verify the extension with original file type.
Copied from a blog
var upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (file.mimetype == "image/png" || file.mimetype == "image/jpg" || file.mimetype == "image/jpeg") {
cb(null, true);
} else {
cb(null, false);
return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
}
}
});
Blog Link
If this doesn't work for you, consider using a separate module which verify file type from buffer data.

Basically what you need is something which can verify the extension with original file type.
Copied from a blog
var upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (file.mimetype == "image/png" || file.mimetype == "image/jpg" || file.mimetype == "image/jpeg") {
cb(null, true);
} else {
cb(null, false);
return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
}
}
});
Blog Link
If this doesn't work for you, consider using a separate module which verify file type from buffer data.

Related

How to delete a video file if the user interrupted the download?

I am doing file uploads using a Multer.
When the file has successfully uploaded, I get the path to the video and can delete it, but if the user interrupted the download, the Multer still uploads part of the file to the downloads folder, but does not give the path to the video file, how can I delete such files in this case ?
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'upload/');
},
filename: function (req, file, cb) {
cb(null, `${req.user.id}-${Date.now()}-${file.originalname}`);
}
});
const fileFilter = (req, file, cb) => {
if(
file.mimetype === 'image/png' ||
file.mimetype === 'image/jpg' ||
file.mimetype === 'image/jpeg' ||
file.mimetype === 'video/mp4'
) {
cb(null, true);
} else {
cb(null, false);
}
};
const uploadConfig = multer({
storage: storage,
fileFilter: fileFilter,
limits: {fileSize: 1000000000 * 10},
preservePath: true
});
router.post('/upload-content', isAuth, uploadConfig.any(), async (req, res, next) => {
try {
res.status(201).json({
message: 'Saved successful',
contentType: req.files[0].mimetype,
fileName: req.files[0].filename,
fileOriginalName: req.files[0].originalname,
fileSize: req.files[0].size,
filePath: req.files[0].path
})
} catch (err) {
console.log(err);
}
})
Unfortunately, this is a multer-issue that has been around for a couple of years now, see this related github issue: https://github.com/expressjs/multer/issues/259
I suggest you try to implement one of the proposed workarounds, we're using the same in our app and it has been working fine so far.

nodejs multer uable to create directory and upload files

I have used middleware - upload which returns error after giving both received files in log:
Error: ENOENT no such file or directory open, C:\{path here}
uploaddata.js:
const express = require("express");
const multer = require("multer");
/* const router = express.Router();
router.use(express.static(__dirname+"uploads/")); */
const Storage = multer.diskStorage({
destination: function(req, file, callback) {
console.log("file.fieldname", file);
callback(null, 'uploads/'+file.fieldname+'/');
},
filename: function(req, file, callback) {
console.log("filename::", file.originalname);
callback(null, file.fieldname + "_" + Date.now() + "_" + file.originalname);
}
});
const upload = multer({
storage: Storage,
limits: {
fileSize: 1024 * 1024
},
fileFilter: (req, file, cb) => {
checkFileType(file, cb);
}
}).fields(
[
{
name:'userpic',
maxCount:1
},
{
name:'usercv',
maxCount:1
}
]
);
function checkFileType(file, cb) {
// console.log("file.fieldname", file);
if(file.fieldname==="userpic")
{
if (file.mimetype === 'image/png' || file.mimetype === 'image/jpg' || file.mimetype === 'image/jpeg')
{ // check file type to be png, jpeg, or jpg
cb(null, true);
} else {
cb(null, false); // else fails
}
}
if(file.fieldname==="usercv")
{
if ( file.mimetype === 'application/pdf' || file.mimetype === 'application/msword' || file.mimetype === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' )
{ // check file type to be pdf, doc, or docx
cb(null, true);
} else {
cb(null, false); // else fails
}
}
}
module.exports = upload;
in console via postman I am sending image and docx file which is reading successfully but it unable to create directory and those files on server path
can any one gives me solution..

file filter function is not working in multer?

I am uploading single file using multer.fileFilter function is not working,it is not even being called by multer.Multer does not care for file extension whether they are allowed or not and upload all files.
Every thing from frontend is correct like(multipart/form-data, and name of input field is image). Here is the my code
This is filefilter functon:
const filefilter = (req, file, cb) =>{
console.log('filefilter executed');
if (
file.mimetype === 'image/png' ||
file.mimetype === 'image/jpg' ||
file.mimetype === 'image/jpeg'
) {
cb(null, true);
console.log('image type correct');
}else {
console.log('image type incorrect');
cb('error Message', false);
}
}
and here is how I used it in multer:
app.use(multer({storage : fileStorage, filefilter: filefilter }).single('image'));
It was Spelling mistake. In ' filefilter ' key 'f' should be capital, so instead of using:
app.use(multer({storage : fileStorage, filefilter: filefilter }).single('image'));
use following:
app.use(multer({storage : fileStorage, fileFilter: filefilter }).single('image'));

Multer file filtering based on MIME type

I have the code below. I would like use multer to accept only documents with the MIME type for docx. Otherwise, I would like to produce an error. I plan on doing vetting on the front-end as well, but for security purposes would like to implement here as well. The following code is returning an error, can anybody tell me where i am wrong?
const multer = require('multer')
const fs = require('fs')
const upload = multer({
dest: './upload',
fileFilter: function (req, file, cb) {
if (req.file.mimetype != 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
return cb(new Error('Wrong file type'))
}
cb(null,true)
}
}).single('file');
app.post('/upload', upload, function(req, res) {
console.log(req.file);
};
It worked for me when I used file.mimetype instead of req.file.mimetype.
fileFilter: function (req, file, cb) {
if (file.mimetype != 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
return cb(new Error('Wrong file type'));
}
cb(null, true)
}
Hope that helped.
You can try following code snippet to accept only file type of docx:
const filefilter = (req, file, cb) => {
if (
file.mimetype === "application/msword" ||
file.mimetype ===
"application/vnd.openxmlformatsofficedocument.wordprocessingml.document"
) {
cb(null, true);
} else {
cb(null, false);
}
};

stop the file upload in multer if the user validation fails

The file uploading is done by multer by using this code, but how to stop the file upload when the user validation fails. where to write the user validation part in this code
router.post('/profilePicture',
multer({dest: './uploads/',
rename: function (fieldname, filename,req,res) {
return image = req.body.userId+'-'+dateTime+'-'+randomId();
},
onFileUploadStart: function (file,req,res) {
if(file.mimetype !== 'image/jpg' && file.mimetype !== 'image/jpeg' && file.mimetype !== 'image/png') {
imageUploadDone = false;
return false;
}
//console.log(file.originalname + ' is starting ...');
},
onFileUploadComplete: function (file,req,res) {
//console.log(file.fieldname + ' uploaded to ' + file.path);
if(file.mimetype == 'image/jpg')
extn = '.jpg';
if(file.mimetype == 'image/jpeg')
extn = '.jpeg';
if(file.mimetype == 'image/png')
extn = '.png';
imageUploadDone=true;
}
}),function(req, res) {
upload(req,res,function(err) {
if(imageUploadDone==true){
//console.log(image);
var userInfo = {'userId':req.body.userId,'newImage':address+image+extn,'path':'./uploads/'};
db.profilePicture(userInfo,function(result){
if(result.message == 'image path added'){
res.json({'success':'1','result':{'message':'Profile Picture Updated','imageUrl':address+image+extn},'error':'No Error'});
}
});
}
if(imageUploadDone == false){
res.json({'success':'0','result':{},'error':'file format is not supported'});
}
});
});
i try to validate the user on the events like onFileUploadStart and onFileUploadComplete. if user is not valid still the file gets uploaded to the path.
This is now possible in 1.0.0.
If you want to abort the upload:
multer({
fileFilter: function (req, file, cb) {
if (path.extname(file.originalname) !== '.pdf') {
return cb(new Error('Only pdfs are allowed'))
}
cb(null, true)
}
})
If you want to skip any files that is not pdf:
multer({
fileFilter: function (req, file, cb) {
if (path.extname(file.originalname) !== '.pdf') {
return cb(null, false)
}
cb(null, true)
}
})
What you can do to validate the format of file : In your app.js put this code
const multer = require('multer');
/* defined storage and filename */
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "images");
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString() + "-" + file.originalname);
}
});
/* defined filter */
const fileFilter = (req, file, cb) => {
if (
file.mimetype === "image/png" ||
file.mimetype === "image/jpg" ||
file.mimetype === "image/jpeg"
) {
cb(null, true);
} else {
cb(new Error("File format should be PNG,JPG,JPEG"), false); // if validation failed then generate error
}
};
app.use(
multer({ storage: fileStorage, fileFilter: fileFilter }).single("image")
);
Now whenever your file is uploaded by key 'image' its format will be checked, if not satisfied then error will be generated.
Hope this will help to someone!
If you want to do file related validation, i.e mime type or file size, you can do this with fileFilter.
multer({
fileFilter: function(req, file, cb) {
// file validation...
}
});
The only problem with the above method is that you cannot do validations against the body of the request. req.body is empty inside the fileFilter callback as explained in this Github issue.
There is a workaround for this, which is described in this Github issue. This is not an optimal solution IMO because it forces the client to ensure validation.
Another option is to let the file be saved and then do your validation checks on req.body, if the request is invalid you can use something like del or rimraf to delete the persisted file from disk.
Another way of addressing this problem.
const path = require('path');
multer({
fileFilter: function (req, file, cb) {
var filetypes = /jpeg|jpg/;
var mimetype = filetypes.test(file.mimetype);
var extname = filetypes.test(path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb("Error: File upload only supports the following filetypes - " + filetypes);
}
});
There is a further discussion about this issue on following link.
https://github.com/expressjs/multer/issues/114

Resources