set multiple size limits for multiple fields in multer - node.js

i have a router to handle form submission when multiple images and a video are uploaded, i need to set maximum size limit for images and video, the multer "limits" property accepts only one value, i tried to check for size inside the file filter like in the code below, but apparently i can't access the file's size until it's received..
const DIR = './uploads/';
const MAX_IMAGE_SIZE = 2 * 1024 * 1024;
const MAX_VIDEO_SIZE = 100 * 1024 * 1024;
const storage = multer.diskStorage({
destination: (req, file, cb) => {
fs.mkdirSync(DIR, { recursive: true });
cb(null, DIR);
},
filename: (req, file, cb) => {
const fileName = file.originalname.toLowerCase().split(' ').join('-');
cb(null, uuidv4() + '-' + fileName)
}
});
let upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (file.fieldname == "images") {
if (file.size > MAX_IMAGE_SIZE) {
req.imageValidationError = "Image size can't exceed 2MB";
return cb(req.fileValidationError);
}
if (file.mimetype == "image/png" || file.mimetype == "image/jpg" || file.mimetype == "image/jpeg" || file.mimetype == "image/webp") {
cb(null, true);
} else {
req.imageValidationError = "Unsupported Image Type";
return cb(req.fileValidationError);
}
}
else if (file.fieldname == "video") {
if (file.size > MAX_VIDEO_SIZE) {
req.videoValidationError = "Video size can't exceed 100MB";
return cb(req.fileValidationError);
}
if (file.mimetype == "video/ogg" || file.mimetype == "video/webm" || file.mimetype == "video/mp4" || file.mimetype == "video/ogv") {
cb(null, true);
} else {
req.videoValidationError = "Unsupported video Type";
return cb(req.fileValidationError);
}
}
}
});
what can i do about this?
i'm thinking of having separate "upload" handlers for images and video..but that doesn't seem a clean approach

Related

Nodejs path module and multer file filter

So I'm using multer and created logic for an image file filter but I got stuck because of this one weird hiccup:
const upload = multer({
storage,
fileFilter: (req, file, done) => {
let ext = path.extname(file.originalname);
if (ext !== ".jpg" || ext !== ".jpeg" || ext !== ".png") {
done(new Error("File type is not supported"), false);
return;
}
done(null, true);
},
});
Why doesnt the code above work? It seems to only work when I switch the || for a &&, like this:
const upload = multer({
storage,
fileFilter: (req, file, done) => {
let ext = path.extname(file.originalname);
if (ext !== ".jpg" && ext !== ".jpeg" && ext !== ".png") {
done(new Error("File type is not supported"), false);
return;
}
done(null, true);
},
});
but I can't seem to understand why that's the case, I feel like || should completely work.

Not able to upload files through multer

const multer = require('multer');
// Define file storage
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads');
},
filename: function (req, file, cb) {
cb(
null,
new Date().toISOString().replace(/:/g, '-') + '-' + file.originalname
);
},
});
// Specify file format that can be saved
function fileFilter(req, file, cb) {
if (
file.mimetype === 'image/png' ||
file.mimetype === 'image/jpg' ||
file.mimetype === 'image/jpeg'
) {
cb(null, true);
} else {
cb(null, false);
}
}
const upload = multer({ storage, fileFilter });
// File Size Formatter
const fileSizeFormatter = (bytes, decimal) => {
if (bytes === 0) {
return '0 Bytes';
}
const dm = decimal || 2;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'YB', 'ZB'];
const index = Math.floor(Math.log(bytes) / Math.log(1000));
return (
parseFloat((bytes / Math.pow(1000, index)).toFixed(dm)) + ' ' + sizes[index]
);
};
module.exports = { upload, fileSizeFormatter };

How set a function into limits parameters on multer?

After this question:
CanI upload many files in different folder using multer and a single html form?
I set multer parameters dinamically and the code works on: destination, filename and filter but not on "limits" parameters!
Someone can help me?
var path = require('path');
var multer = require('multer');
var defaultMaxSize = 3 * 1024 * 1024;
var defaultFiles = 1;
exports.upload = function(obj, next) {
var upld = {};
var storage = {};
var limits = {};
for (key in obj) {
switch (key) {
case 'destination':
storage.destination = function(request, file, cb) {
cb(null, obj.destination[file.fieldname]);
};
break;
case 'filename':
storage.filename = function(request, file, cb) {
if (obj.filename[file.fieldname] != undefined) {
nome = obj.filename[file.fieldname];
if (nome == undefined)
nome = file.originalname.substring(0, file.originalname.lastIndexOf("."));
nome += file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
} else nome = file.originalname.split('/').pop().trim();
cb(null, nome);
};
break;
case 'FieldNameSize':
limits.fieldNameSize  = function(request, file, cb) {
if (obj.fieldNameSize[file.fieldname] != undefined)
cb(null, obj.fieldNameSize[file.fieldname]);
else return cb(null, true);
};
break;
case 'fieldSize':
limits.fieldSize = function(request, file, cb) {
if (obj.fieldSize[file.fieldname] != undefined)
cb(null, obj.fieldSize[file.fieldname]);
else return cb(null, true);
};
break;
case 'fields':
limits.fields  = function(request, file, cb) {
if (obj.fields[file.fieldname] != undefined)
cb(null, obj.fields[file.fieldname]);
else return cb(null, true);
};
break;
case 'fileSize':
limits.fileSize = function(request, file, cb) {
if (obj.fileSize[file.fieldname] != undefined)
cb(null, obj.fileSize[file.fieldname]);
else return cb(null, true);
};
break;
case 'files':
limits.files  = function(request, file, cb) {
if (obj.files[file.fieldname] != undefined)
cb(null, obj.files[file.fieldname]);
else return cb(null, true);
};
break;
case 'parts':
limits.parts  = function(request, file, cb) {
if (obj.parts[file.fieldname] != undefined)
cb(null, obj.parts[file.fieldname]);
else return cb(null, true);
};
break;
case 'filetypes':
upld.fileFilter = function(req, file, cb) {
if (obj.filetypes[file.fieldname] != undefined) {
var f = obj.filetypes[file.fieldname];
f = f.replace(",", "|");
var re = new RegExp(f);
var mimetype = re.test(file.mimetype);
var extname = re.test(path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb("Sono Accettate solo le seguenti estensioni: " + obj.filetypes[file.fieldname]);
} else return cb(null, true);
};
break;
}
}
if (storage.filename == undefined)
storage.filename = function(request, file, cb) {
nome = file.originalname.split('/').pop().trim();
cb(null, Date.now() + "_" + nome);
};
if (limits.fileSize == undefined)
limits.fileSize = defaultMaxSize;
/*
var maxSize=l.maxSize;
limitsfileSize= function (request, file, cb) {
if(file.size>(maxSize)) { cb(null,true); }
else { cb("Il file non può pesare più di"+maxSize+" MB" ); }
};
*/
upld.storage = multer.diskStorage(storage);
upld.limits = limits;
return multer(upld).any();
};

Uploading multiple files with multer, but from different fields?

How can I have multer accept files from multiple file type fields?
I have the following code that uploads a single file, using multer in node.js:
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './public/uploads');
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '-' + Date.now());
}
});
var upload = multer({ storage : storage });
app.post('/rest/upload', upload.array('video', 1), function(req, res, next){
...
}
From the following form, on the condition only the video field has a value (if I specify both I get an 'Unexpected field' error):
<form action="/rest/upload" method="post" enctype="multipart/form-data">
<label>Video file: </label> <input type="file" name="video"/>
<label>Subtitles file: </label> <input type="file" name="subtitles"/>
<input type="submit"/>
</form>
It is not clear from the documentation how to approach this? Any suggestions would be appreciated. BTW I have tried the following parameter variations, without success:
app.post('/rest/upload', [upload.array('video', 1), upload.array('subtitles', 1)] ...
app.post('/rest/upload', upload.array('video', 1), upload.array('subtitles', 1), ...
app.post('/rest/upload', upload.array(['video', 'subtitles'], 1), ...
What you want is upload.fields():
app.post('/rest/upload',
upload.fields([{
name: 'video', maxCount: 1
}, {
name: 'subtitles', maxCount: 1
}]), function(req, res, next){
// ...
}
Using Multer Upload Files From Two Fields of Separate Forms on Different Pages
In this example I have two fields - resume and image. Resume in one form and Image in other. Both are on separate pages.
First import dependencies
const path = require('path'); // for getting file extension
const multer = require('multer'); // for uploading files
const uuidv4 = require('uuidv4'); // for naming files with random characters
Define fileStorage and fileFilter:
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => { // setting destination of uploading files
if (file.fieldname === "resume") { // if uploading resume
cb(null, 'resumes');
} else { // else uploading image
cb(null, 'images');
}
},
filename: (req, file, cb) => { // naming file
cb(null, file.fieldname+"-"+uuidv4()+path.extname(file.originalname));
}
});
const fileFilter = (req, file, cb) => {
if (file.fieldname === "resume") { // if uploading resume
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
}
} else { // else uploading image
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
}
}
};
Middleware for multer
app.use(
multer(
{
storage: fileStorage,
limits:
{
fileSize:'2mb'
},
fileFilter: fileFilter
}
).fields(
[
{
name: 'resume',
maxCount: 1
},
{
name: 'image',
maxCount: 1
}
]
)
);
And then call your routes. You may need to add csrf protection or authentication along with this for security. But this should work fine.
If you want to upload multiple files/images from the same form, I have used the below code and it works fine. The path of the image is stored in the database; I will skip the database path and go straight to the upload function and how the fields are passed to the save function.
const path = require('path');
const multer = require('multer');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
if (file.fieldname === "profile") {
cb(null, './uploads/profiles/')
}
else if (file.fieldname === "natid") {
cb(null, './uploads/ids/');
}
else if (file.fieldname === "certificate") {
cb(null, './uploads/certificates/')
}
},
filename:(req,file,cb)=>{
if (file.fieldname === "profile") {
cb(null, file.fieldname+Date.now()+path.extname(file.originalname));
}
else if (file.fieldname === "natid") {
cb(null, file.fieldname+Date.now()+path.extname(file.originalname));
}
else if (file.fieldname === "certificate") {
cb(null, file.fieldname+Date.now()+path.extname(file.originalname));
}
}
});
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 10
},
fileFilter: (req, file, cb) => {
checkFileType(file, cb);
}
}).fields(
[
{
name:'profile',
maxCount:1
},
{
name: 'natid', maxCount:1
},
{
name: 'certificate', maxCount:1
}
]
);
function checkFileType(file, cb) {
if (file.fieldname === "certificate") {
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
}
}
else if (file.fieldname === "natid" || file.fieldname === "profile") {
if (
file.mimetype === 'image/png' ||
file.mimetype === 'image/jpg' ||
file.mimetype === 'image/jpeg'||
fiel.mimetype==='image/gif'
) { // check file type to be png, jpeg, or jpg
cb(null, true);
} else {
cb(null, false); // else fails
}
}
}
//at the save function
upload(req, res, (err) => {
if (err) {
console.log(err);
} else {
if (req.file == "undefined") {
console.log("No image selected!")
} else {
let datecreated = new Date();
let fullnames = req.body.firstname + ' ' + req.body.lastname;
let formatedphone = '';
let phone = req.body.personalphone;
if (phone.charAt(0) == '0') {
formatedphone = '+254' + phone.substring(1);
} else if ((phone.charAt(0) == '+') && (phone.length > 12 || phone.length <= 15)) {
formatedphone = phone
}
let teachers = {
"teacherid": teacherid,
"schoolcode": req.body.schoolcode,
"fullnames": fullnames,
"email": req.body.email,
"dateofbirth": req.body.dateofbirth,
"nationalid": req.body.nationalid,
"personalphone": formatedphone,
"profile": req.files.profile[0].path,
"natid": req.files.natid[0].path,
"certificate":req.files.certificate[0].path
}
connection.query('INSERT INTO teachers SET ?', teachers, (error, results, fields) => {`enter code here`
if (error) {
res.json({
status: false,
message: 'there are some error with query'
})
console.log(error);
} else {console.log("Saved successfully");
}
this worked for Me. complete example
var multer = require('multer')
var storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, './public/audio');
},
filename: function(req, file, callback) {
console.log(file);
if(file.originalname.length>6)
callback(null, file.fieldname + '-' + Date.now() + file.originalname.substr(file.originalname.length-6,file.originalname.length));
else
callback(null, file.fieldname + '-' + Date.now() + file.originalname);
}
});
const upload = multer({ storage: storage });
router.post('/save/audio',upload.fields([{
name: 'audio', maxCount: 1
}, {
name: 'graphic', maxCount: 1
}]) ,(req, res) => {
const audioFile = req.files.audio[0];
const audioGraphic = req.files.graphic[0];
const fileName = req.body.title;
saveAudio(fileName,audioFile.filename,audioGraphic.filename,req.body.artist,function (error,success) {
req.flash('success','File Uploaded Successfully')
res.redirect('/')
});
})
Did you try to use multer().any()?
I just need upload fields store in a arry
const express = require("express");
const category_route = express();
const bodyParser = require('body-parser');
category_route.use(bodyParser.json());
category_route.use(bodyParser.urlencoded({extended:true}));
const controller = require('../Controller/Category');
const Multer = require('multer')
const Path = require('path');
const multer = require("multer");
category_route.use(express.static('public'));
 const storage = multer.diskStorage({
    destination : function(req,files,cb){
        cb(null,Path.join(__dirname,'../public/category'),function(err,sucess){
            if(err){
                throw err;
            }
        });
    },
    filename:function(req,files,cb){
        const name = Date.now()+'-'+ files.originalname;
        cb(null,name, function(err, sucess){
            if(err){
                throw err;
            }
        });
    } 
 });
 const upload = multer({storage:storage})
category_route.post('/add-category',upload.fields([
    {
      name: "icon",
      maxCount: 1,
    },
    {
      name: "banner",
      maxCount: 1,
    }
  ]), controller.addCategory);
module.exports = category_route;
/* controller code*/
const Category = require("../Model/Category");
const addCategory = async (req, res) => {
  try {
    var arrIcon = [];
    for(let i=0; i<req.files.length; i++){
      arrIcon[i] = req.files[i].filename;
    }
    var arrBanner = [];
    for(let j=0; j<req.files.length; j++){
      arrBanner[j] = req.files[j].filename;
    }
    const catData = await Category.find();
    
    if (catData.length > 0) {
      let checking = false;
      catData.every((i) => {
        if (i.name.toLowerCase() === req.body.name.toLowerCase()) {
          checking = true;
          console.log("FOUND");
          return false;
        }
        console.log("NOT-FOUND");
        return true;
      });
      if (checking === false) {
        const data = new Category({
          name: req.body.name,
          camission: req.body.camission,
          icon: arrIcon,
          banner: arrBanner,
          mtitel: req.body.mtitel,
          mdiscp: req.body.mdiscp,
        });
        const result = await data.save();
        res.send(result);
      } else {
        res.send("Category is Already exieet");
      }
    } else {
      const data = new Category({
        name: req.body.name,
        camission: req.body.camission,
        icon: arrIcon,
        banner: arrBanner,
        mtitel: req.body.mtitel,
        mdiscp: req.body.mdiscp,
      });
      const result = await data.save();
      res.send(result);
    }
  } catch (error) {
    console.log(error);
    res.send("somthing Wrong");
  }
};
module.exports = { addCategory };
upload(req, res, (err) => {
if (err) {
console.log(err);
} else {
if (req.file == "undefined") {
console.log("No image selected!")
} else {
let datecreated = new Date();
let fullnames = req.body.firstname + ' ' + req.body.lastname;
let formatedphone = '';
let phone = req.body.personalphone;
if (phone.charAt(0) == '0') {
formatedphone = '+254' + phone.substring(1);
} else if ((phone.charAt(0) == '+') && (phone.length > 12 || phone.length <= 15)) {
formatedphone = phone
}
let teachers = {
"teacherid": teacherid,
"schoolcode": req.body.schoolcode,
"fullnames": fullnames,
"email": req.body.email,
"dateofbirth": req.body.dateofbirth,
"nationalid": req.body.nationalid,
"personalphone": formatedphone,
"profile": req.files.profile[0].path,
"natid": req.files.natid[0].path,
"certificate":req.files.certificate[0].path
}
connection.query('INSERT INTO teachers SET ?', teachers, (error, results, fields) => {
if (error) {
res.json({
status: false,
message: 'there are some error with query'
})
console.log(error);
} else {
console.log('Saved successfully');}

How to limit the file size when uploading with multer?

I'm making a simple file upload system with multer:
var maxSize = 1 * 1000 * 1000;
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, 'public/upload');
},
filename: function (req, file, callback) {
callback(null, file.originalname);
},
onFileUploadStart: function(file, req, res){
if(req.files.file.length > maxSize) {
return false;
}
}
});
var upload = multer({ storage : storage}).single('bestand');
router.post('/upload',function(req,res){
upload(req,res,function(err) {
if(err) {
return res.end("Error uploading file.");
}
console.log(req.file);
res.redirect(req.baseUrl);
});
});
This all works fine and the file gets uploaded. The only thing that is not working is the limit on the max size. I made it so that onfileupload start the size of the file gets checked and if its to big it will return false. But the file still just gets uploaded.
It seems that onFileUploadStart isn't doing anything at all. I tried to console.log something in it, but nothing.
What am I doing wrong? How can I limit the file size when uploading with multer?
There is no onFileUploadStart with the new multer API. If you want to limit the file size, you should instead add limits: { fileSize: maxSize } to the object passed to multer():
var upload = multer({
storage: storage,
limits: { fileSize: maxSize }
}).single('bestand');
I think it is you are looking.
Have a great day.
const fileFilterMiddleware = (req, file, cb) => {
const fileSize = parseInt(req.headers["content-length"])
if ((file.mimetype === "image/png" || file.mimetype === "image/jpg" || file.mimetype === "image/jpeg" || file.mimetype === "application/octet-stream") && fileSize <= 1282810) {
cb(null, true)
} else if (file.mimetype === "video/mp4" && fileSize <= 22282810) {
cb(null, true)
} else {
cb(null, false)
}
}
For anyone who uses Multer with Ts.ED. When I tried to upload a too large file (no matter what extension it had), I ended up with the following error showing up:
Cannot read properties of undefined (reading 'replace')
I did use the following code to fix this:
#Configuration({
...
multer: {
limits: {
fieldNameSize: 300,
fileSize: 1048576, // 10 Mb
},
fileFilter: (req, file, callback) => {
const acceptableExtensions = ['.png', '.jpg'];
if (!(acceptableExtensions.includes(Path.extname(file.originalname)))) {
return callback(new Error('...'));
}
// added this
const fileSize = parseInt(req.headers['content-length']);
if (fileSize > 1048576) {
return callback(new Error('...'));
}
// --
callback(null, true);
}
}
})
Now it works.

Resources