Multer: fileFilter not working - node.js

I'm using multer to upload files. everything is goung fine but fileFilter is not working. I've no idea where i'm going wrong.
routes.js
var multer = require('multer');
// Multer diskStorage setting
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './uploads/');
},
filFilter: function (req, file, cb) {
var typeArray = file.mimetype.split('/');
var fileType = typeArray[1];
if (fileType == 'jpg' || fileType == 'png') {
cb(null, true);
} else {
cb(null, false)
}
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '_' + Date.now() + '.jpg');
}
});
var upload = multer({ storage: storage });
router.post('/add', upload.single('photo'), function(req, res){
console.log('uploaded');
}
Multer also uploads files other than allowed. Any idea?

fileFilter must be defined in multer's options object rather than diskStorage
const uploadFilter = function(req, file, cb) {
// filter rules here
}
const upload = multer({
storage: storage,
fileFilter: uploadFilter,
});

const storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './uploads/');
},
fileFilter: function (req, file, cb) {
const extension = path.extname(file.originalname).toLowerCase();
const mimetyp = file.mimetype;
if (
extension !== '.jpg' ||
extension !== '.jpeg' ||
extension !== '.png' ||
mimetyp !== 'image/png' ||
mimetyp !== 'image/jpg' ||
mimetyp !== 'image/jpeg'
) {
cb('error message', true);
}
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '_' + Date.now() + '.jpg');
},
});
const upload = multer({ storage: storage });
router.post('/add', upload.single('photo'), function (req, res) {
console.log('uploaded');
});
i think this will help

You have a typo at your fileFilter; you've named it filFilter.
Could you change your code to the following and tell us if it worked out?
var multer = require('multer');
// Multer diskStorage setting
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './uploads/');
},
fileFilter: function (req, file, cb) {
var typeArray = file.mimetype.split('/');
var fileType = typeArray[1];
if (fileType == 'jpg' || fileType == 'png') {
cb(null, true);
} else {
cb(null, false)
}
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '_' + Date.now() + '.jpg');
}
});
var upload = multer({ storage: storage });
router.post('/add', upload.single('photo'), function(req, res){
console.log('uploaded');
}

Related

post multiple field of file upload in different folder in node js api using multer

I am using multer for single file upload in 1 post field but what if i want to add 2 or more file inside different post field like below.
this is for different module.
so for single file upload i am using following code
var uploadfilename = "";
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './public/assets' + customStrings.FOLDER)
},
filename: (req, file, cb) => {
uploadfilename = Date.now() +
path.extname(file.originalname);
cb(null, uploadfilename);
}
});
var upload = multer({
storage: storage
});
and in post api route
router.post("/news", upload.single('image'), (req, res) => {
});
now if i want multiple field for file upload what we will do ??
i fount one solution for that is following
var uploadfilename = "";
var uploadCityfilename = "";
var storage = multer.diskStorage({
destination: (req, file, cb) => {
if (file.fieldname == "image") {
cb(null, './public/assets' + customStrings.FOLDER)
} else {
cb(null, './public/assets' + customStrings.CITY_FOLDER)
}
},
filename: (req, file, cb) => {
if (file.fieldname == "image") {
uploadfilename = Date.now() + "-" +
customStrings.IMAGE_POST_NAME + path.extname(file.originalname);
cb(null, uploadfilename);
} else {
uploadCityfilename = req.body.city + path.extname(file.originalname);
cb(null, uploadCityfilename);
}
}
});
var upload = multer({
storage: storage
});
and in post api route
router.post("/imageupload", upload.any(), (req, res) => {
});
it works for me :)

req.body not working when using multipart/form-data in html form NodeJs/Express

I am using body-parser to get info requested from forms, but when I put enctype="multipart/form-data" in the header of the form, the req.body will not working at all.
However, I have used multer lib in order to upload images as follow:
app.post("/", function (req, res, next) {
var button = req.body.button_name;
if (button == "upload") {
var UserId = 2;
var imageUploadedLimited = 3;
var storage = multer.diskStorage({
destination: function (req, file, callback) {
var dir = "./public/images/uploads/";
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
dir = "./public/images/uploads/" + UserId;
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
callback(null, dir);
},
filename: function (req, file, cb) {
cb(null, file.fieldname + "-" + Date.now() + ".jpg");
},
});
const maxSize = 1 * 1000 * 1000;
var upload = multer({
storage: storage,
limits: { fileSize: maxSize },
fileFilter: function (req, file, cb) {
var filetypes = /jpeg|jpg|png/;
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
);
},
}).array("filename2", imageUploadedLimited);
upload(req, res, function (err) {
if (err) {
res.send(err);
} else {
res.send("Success, Image uploaded!");
}
});
}
});
If I print out the button variable from the second line, will show me undefined value. I know body-parser does not support enctype="multipart/form-data". In this case, how I can see the value from the form?
If you are using multer, it needs to be passed as middleware
// Do Something like this
var multer = require('multer')
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'Your path here')
},
filename: function (req, file, cb) {
cb(null, "fileName")
}
})
var upload = multer({
storage: storage,
})
app.post("/" ,upload.any(), function (req, res, next) {
// NOW YOU CAN ACCESS REQ BODY HERE
});

Multer fileFilter is not getting called and req.body is empty

for some reason the fileFilter on multer is not getting called.
here is my Controller (i am using express routers)
const express = require('express');
const router = express.Router();
const UploadController = require('../controllers/UploadController');
router.route('/upload').post(UploadController.upload);
module.exports = router;
and this is the controller
const multer = require('multer');
const fs = require('fs');
module.exports = {
upload: function (req, res) {
let storage = multer.diskStorage({
destination: function (req, file, cb) {
console.log('here');
const filesDir = './uploads/' + req.body.ref;
if (!fs.existsSync(filesDir)) {
fs.mkdirSync(filesDir);
}
cb(null, filesDir);
},
filename: function (req, file, cb) {
let extArray = file.mimetype.split("/");
let extension = extArray[extArray.length - 1];
cb(null, req.body.type + '-' + Date.now() + '.' + extension);
},
fileFilter : function (req, file, cb) {
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return cb(new Error('Only image files are allowed!'), false);
}
cb(null, true);
}
})
const upload = multer({ storage: storage }).single('file');
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
res.send({
error: err
});
} else if (err) {
res.send({
error: err
});
}else{
res.send({
file: req.file,
body: req.body
});
}
});
}
}
I have following issues:
The fileFilter function is not even called so its not validating files
the req.body on the upload function (upload: function (req, res)) is empty it is only available in diskStorage and last upload function (upload(req, res, function (err)) so i cannot validate the body data also.
I had the same problem. the fileFilter function has to be defined inside multer, not inside the diskStorage function.
To make it a bit more readable I defined the storage and filter in variables instead of making everything inside the multer call.
// storage settings
const multerStorage = multer.diskStorage({
destination: function(req, file, next) {
next(null, './public/files');
},
filename: function(req, file, next) {
const sanitizedName = file.originalname
.replace('/[^a-z0-9\./gi', '-')
.replace('/-{2,}/g', '-')
.toLowerCase();
const name = Date.now() + '-' + sanitizedName;
// sending the file name to be stored in the database
req.body.filename = name;
next(null, name);
},
limits: {
fileSize: 25000000
}
});
// filter function
const multerFilter = function(req, file, cb) {
const ext = path.extname(file.originalname).toLowerCase();
if (ext !== '.pdf') {
cb(new Error('File must be in PDF format.'));
}
cb(null, true);
}
And then applying the storage settings and filter function to multer:
const upload = multer({
storage: multerStorage,
fileFilter: multerFilter
});

Multer callback when file is uploaded in the disk completely

I have written the code for multer as shown below:
const storage = multer.diskStorage({
destination(req, file, callback) {
callback(null, './public/images')
},
filename(req, file, callback) {
profile_image = `${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`;
callback(null, profile_image)
}
});
const xyz = (req, res) => {
upload = multer({
limits: {
fileSize: 1000000,
files: 2
},
storage,
fileFilter(req, file, callback) {
const ext = path.extname(file.originalname);
if (ext !== '.png' && ext !== '.jpg' && ext !== '.gif' && ext !== '.jpeg') {
return callback(res.end('Only images are allowed'), null)
}
callback(null, true);
}
}).any();
upload(req, res, err => {
})
upload starts working the moment there is no error in uploading file, but not when the file is uploaded successfully in the disk. So is there any other callback through which I can know when the file is successfully uploaded in the disk?
Try this it might solves your issue. You can check success in upload function in below example.
var Express = require('express');
var multer = require('multer');
var bodyParser = require('body-parser');
var app = Express();
app.use(bodyParser.json());
var Storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, "./Images");
},
filename: function (req, file, callback) {
callback(null, file.fieldname + "_" + Date.now() + "_" + file.originalname);
}
});
var upload = multer({ storage: Storage }).array("imgUploader", 3); //Field name and max count
app.get("/", function (req, res) {
res.sendFile(__dirname + "/index.html");
});
app.post("/api/Upload", function (req, res) {
upload(req, res, function (err) {
if (err) {
return res.end("Something went wrong!");
}
return res.end("File uploaded sucessfully!.");
});
});
app.listen(2000, function (a) {
console.log("Listening to port 2000");
});
Check in your upload function like this
upload(req, res, err => {
if (err) {
res.end("Something went wrong!");
}
res.end("File uploaded sucessfully!.");
}

How to store a file with file extension with multer?

Managed to store my files in a folder but they store without the file extension.
Does any one know how would I store the file with file extension?
I have a workaround for the adding proper extension of files. If you use path node module
var multer = require('multer');
var path = require('path')
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + path.extname(file.originalname)) //Appending extension
}
})
var upload = multer({ storage: storage });
From the docs: "Multer will not append any file extension for you, your function should return a filename complete with an file extension."
Here's how you can add the extension:
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '.jpg') //Appending .jpg
}
})
var upload = multer({ storage: storage });
I would recommend using the mimetype property to determine the extension. For example:
filename: function (req, file, cb) {
console.log(file.mimetype); //Will return something like: image/jpeg
More info: https://github.com/expressjs/multer
I got file the extension from file.mimetype .
I split the mimetype and get the file extension from it
Please try the below function.
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads')
},
filename: function (req, file, cb) {
let extArray = file.mimetype.split("/");
let extension = extArray[extArray.length - 1];
cb(null, file.fieldname + '-' + Date.now()+ '.' +extension)
}
})
const upload = multer({ storage: storage })
It can be done like this:
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, config.DIR)
},
filename: function (req, file, cb) {
let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
cb(null, Date.now() + ext)
}
});
const upload = multer({
storage: storage
}).any();
import multer from 'multer';
import * as shortid from 'shortid';
import * as mime from 'mime-types';
const storage = multer.diskStorage({
destination: function (req,file,cb) {
cb(null, '/path/to/uploads/');
},
filename: function (req,file,cb) {
/* generates a "unique" name - not collision proof but unique enough for small sized applications */
let id = shortid.generate();
/* need to use the file's mimetype because the file name may not have an extension at all */
let ext = mime.extension(file.mimetype);
cb(null, `${id}.${ext}`);
}
});
EDIT
shortid has been deprecated you should use nanoid.
import multer from 'multer';
import * as nanoid from 'nanoid';
import * as mime from 'mime-types';
const storage = multer.diskStorage({
destination: function (req,file,cb) {
cb(null, '/path/to/uploads/');
},
filename: function (req,file,cb) {
/* generates a "unique" name - not collision proof but unique enough for small sized applications */
let id = nanoid();
/* need to use the file's mimetype because the file name may not have an extension at all */
let ext = mime.extension(file.mimetype);
cb(null, `${id}.${ext}`);
}
});
There may be some issues in the already answered codes.
There may be some cases of files with no extension.
There should not be an upload.any() usage. Its vulnerable to the attackers
The upload function should not be global
.
I have written the below codes for better security.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'temp/')
},
filename: function (req, file, cb) {
let ext = ''; // set default extension (if any)
if (file.originalname.split(".").length>1) // checking if there is an extension or not.
ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
cb(null, Date.now() + ext)
}
})
var upload = multer({ storage: storage });
Using it for upload
// using only single file object name (HTML name attribute)
// May use upload.array(["file1","file2"]) for more than one
app.post('/file_upload', upload.single("file"), function (req,res) {
//console.log(req.body, 'Body');
console.log(req.file, 'file');
res.send("cool");
})
I used this little trick to get file extension, and as a workaround to circumvent issues that might occur when someone uploads a file with similar file name twice, or that exists in the server.
const path = require('path');
const crypto = require('crypto');
let upload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, path.join(__dirname, '../uploads'))
},
filename: (req, file, cb) => {
// randomBytes function will generate a random name
let customFileName = crypto.randomBytes(18).toString('hex')
// get file extension from original file name
let fileExtension = path.extname(file.originalname).split('.')[1];
cb(null, customFileName + '.' + fileExtension)
}
})
})
const multer = require('multer');
const uuid = require('uuid/v1');
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpeg',
'image/jpg': 'jpg'
};
const fileUpload = multer({
limits: 500000,
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/images');
},
filename: (req, file, cb) => {
const ext = MIME_TYPE_MAP[file.mimetype];
cb(null, uuid() + '.' + ext);
}
}),
fileFilter: (req, file, cb) => {
const isValid = !!MIME_TYPE_MAP[file.mimetype];
let error = isValid ? null : new Error('Invalid mime type!');
cb(error, isValid);
}
});
module.exports = fileUpload;
The file extension can be dynamic.
here is the solution
const path = require('path'); // path for cut the file extension
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads')
},
filename: function (req, file, cb) {
cb(null, 'upload_at_' + Date.now() + path.extname(file.originalname))
}
})
I use this method and it works.
I store the file in this format:
FieldName+Date+Extension => Profile1621416613594.jpg
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req,file,cb){
cb(null, './uploads')
},
filename: function (req,file,cb){
cb(null,file.fieldname+'-'+Date.now()+'.'+file.mimetype.split('/').reverse()[0]);
},
});
var upload = multer({storage: storage});
I am doing like this
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/img/')
},
filename: function (req, file, cb) {
let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
cb(null, Date.now() + ext);
}
})
var upload = multer({ storage: storage }).single('eventimage');
An object oriented way to store image with unique name
// image.service.ts
import { diskStorage, StorageEngine } from "multer";
class ImageStorageService {
storage: StorageEngine
constructor() {
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpg',
'image/jpg': 'jpg'
}
this.storage = diskStorage({
destination: (req, file, callback) => {
const isValid = MIME_TYPE_MAP[file.mimetype]
let error = new Error(`Invalid mime type`)
if (isValid)
error = null
//app.use(express.static(path.join(`${__dirname}/assets`)))
callback(error, 'assets/images')
},
filename: (req, file, callback) => {
let currentFileName: string = file.originalname.substr(0, file.originalname.lastIndexOf('.'))
const name = currentFileName.toLowerCase().split(' ').join('-')
const ext = MIME_TYPE_MAP[file.mimetype]
callback(null, `${name}-${Date.now()}.${ext}`)
}
})
}
}
export const ImageStorage = new ImageStorageService().storage
then in one of your routes
import { ImageStorage } from "./services/image-storage.service";
this.router.post('/signup', multer({ storage: ImageStorage }).single('image'), async (req, res, next) => {
let img_url: string
if (req.file) {
const url: string = `${req.protocol}:\/\/${req.get('host')}`
img_url = url + '/images/' + req.file.filename
//http://localhost:3000/images/penguins-1548339248380.jpg
}
})
I like to use the original filename for SEO purposes. This requires a bit more checking if the file with the same name already exists.
Moreover, extension resolving is done in a few steps to provide maximum flexibility.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
// try to get extension from original file name
var lioDot = file.originalname.lastIndexOf('.');
if (lioDot !== -1) {
// I like to use original upload filename for SEO but first lets clean it
var newName = file.originalname.substring(0, lioDot).replace(/([^a-z0-9]+)/gi, '-');
var ext = file.originalname.substring(lioDot, file.originalname.length);
} else {
var newName = file.originalname.replace(/([^a-z0-9]+)/gi, '-');
// try to get extension from mime type string
var extArray = file.mimetype.split("/");
var ext = extArray[extArray.length - 1];
// mime type extension resolving by pure string extraction is not accurate for a lot of types
// https://www.freeformatter.com/mime-types-list.html
// it's usually fine for ext strings up to 4 characters, png, jpeg, gif, bmp, tiff ..
if (ext > 4) {
// other mime types you would like to support
var mimetypes = { 'vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx' };
if (mimetypes.hasOwnProperty(ext)) ext = mimetypes[ext];
}
}
var newFullName = newName + ext;
var i = 0;
// we need to check if the file with the same name already exists
// if it exists then we're adding something to make it unique
while (fs.existsSync(process.env.PWD + '/uploads/' + newFullName)) {
newFullName = newName + '-' + ++i + ext;
}
cb(null, newFullName);
}
})
const upload = multer({ storage: storage });
It can be done like this...simple to grasp
// validate uploaded files
const FILE_TYPE_MAP = {
// mime type
"image/png": "png",
"image/jpeg": "jpeg",
"image/jpg": "jpg",
};
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public/uploads");
},
filename: function (req, file, cb) {
const filename = file.originalname.replace(" ", "-");
const extension = FILE_TYPE_MAP[file.mimetype]
cb(null, `${filename}-${Date.now()}.${extension}`);
},
});
Simple helpler function that maintains the unique filename generated by multer and adds the extension parsed from mimetype:
Just pass the object returned by multer
const fs = require('fs');
function renameWithExt(file) {
const ext = file.mimetype.split('/')[1]; // parse the extension type
fs.rename(`${file.path}`, `${file.path}.${ext}`, () => {
console.log(`File: ${file.filename} renamed with extension '.${ext}'`);
});
}
renameWithExt(req.file);
const multer = require('multer');
const uuid = require('uuid/v1');
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpeg',
'image/jpg': 'jpg'
};
const fileUpload = multer({
limits: 500000,
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/images');
},
filename: (req, file, cb) => {
const ext = MIME_TYPE_MAP[file.mimetype];
cb(null, uuid() + '.' + ext);
}
}),
fileFilter: (req, file, cb) => {
const isValid = !!MIME_TYPE_MAP[file.mimetype];
let error = isValid ? null : new Error('Invalid mime type!');
cb(error, isValid);
}
});
module.exports = fileUpload;

Resources