How to catch errors in multer - node.js

I have read documentation for multer. But the current set up I have in my code is different and this makes difficult for me to understand how to handle errors.
It is important now because it happened (only once) that a file was not stored on the server but the code continued saving info in database as if the storing of the file had worked. But it probably did not.
const multer = require('multer');
var docPath = "path_to_disk_where_to_store_files";
var storage = multer.diskStorage({
inMemory: true,
destination: function (request, file, callback) {
callback(null, docPath);
},
filename: function (request, file, callback) {
//Just a function that creates an unique name with timestamp
renamedFile = helpers.createUniqueName(file.originalname);
callback(null, renamedFile);
}
});
var fileFilter = function (req, file, cb) {
var path = require('path');
var ext = path.extname(file.originalname);
if (file.mimetype !== 'application/pdf' || ext.toLowerCase() != '.pdf') {
req.fileValidationError = 'goes wrong on the mimetype';
return cb(null, false, new Error('goes wrong on the mimetype'));
}
cb(null, true);
};
const multerUploader = multer({storage: storage, fileFilter: fileFilter, limits: { fileSize: maxSize }});
router.post('/save_document',[multerUploader.single('file'),saveDocumentInDb]);
I dont really understand where the if-statement that will check if the upload got an error would fit.

Please refer the following for error handling when using multer:
https://github.com/expressjs/multer#error-handling
Your implementation will be something like this:
const multerUploader = multer({storage: storage, fileFilter: fileFilter, limits: { fileSize: maxSize }});
const upload = multerUploader.single('file');
router.post('/save_document', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
} else if (err) {
// An unknown error occurred when uploading.
}
// Everything went fine and save document in DB here.
})
})

Related

Multer error cannot upload file with size above 50kb

I'm having an issue with multer since I'm using socket.io(I'm not sure but after I use this my multer middleware did not work properly). Here is my code for more information
const maxSize = 1024 * 1024 * 2
const storage = multer.diskStorage({
destination: function (_req, _file, cb) {
cb(null, path.join(process.cwd(), 'assets', 'pictures'))
},
filename: function (_req, file, cb) {
const ext = file.originalname.split('.')[1]
const date = new Date()
cb(null, `${date.getTime()}.${ext}`)
}
})
const upload = multer({
storage: storage,
limits: { fileSize: maxSize }
}).single('picture')
const uploadFilter = (req, res, next) => {
console.log(req, 'test mmulter first')
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return response(res, false, err.message, 400)
} else if (err) {
return response(res, false, err.message, 500)
}
// console.log(req, 'test multer second')
next()
})
}
So when I try to upload a file under 50kb the upload works perfectly, but when I try to put a file with a size of more than 50kb it will keep hanging, no return any response even it no returns an error, so when this error happens the console.log() and next() inside upload not called. The weird thing is the file indeed saved to my local folder (either 50kb or greater) but when it came to the file with size above 50kb it will cropped.
like this
enter image description here

How to handle erros from multer custom storage?

I'm using multer with sharp and a custom storage, image upload is set and it works fine but I can not handle the errors correctly.
It is crashing my server when I upload for example a wrong file type or when a file is too big.
on my app.js
const upload = multer({
storage: new customStorage({
destination: function(req, file, cb) {
cb(
null,
path.join(
__dirname,
'/images',
new Date().toISOString().replace(/:/g, '-') +
'-' +
file.originalname.replace(/\s+/g, '-')
)
);
}
}),
limits: { fileSize: 5000000 }
});
on my customStorage.js
const fs = require('fs');
const sharp = require('sharp');
const nodePath = require('path');
function getDestination(req, file, cb) {
cb(null, 'images');
}
function customStorage(opts) {
this.getDestination = opts.destination || getDestination;
}
customStorage.prototype._handleFile = function _handleFile(req, file, cb) {
this.getDestination(req, file, function(err, path) {
if (err) return cb(err);//***the problem is here.***
const outStream = fs.createWriteStream(path);
const transform = sharp().resize(200, 200);
file.stream.pipe(transform).pipe(outStream);
outStream.on('error', cb);
outStream.on('finish', function() {
cb(null, {
path: 'images/' + nodePath.basename(path),
size: outStream.bytesWritten
});
});
});
};
customStorage.prototype._removeFile = function _removeFile(req, file, cb) {
fs.unlink(file.path, cb);
};
module.exports = function(opts) {
return new customStorage(opts);
};
When i upload another file it says:
Error: Input buffer contains unsupported image format
Emitted 'error' event at:
at sharp.pipeline (/Users/David/nodejs-app/node_modules/sharp/lib/output.js:687:18)
I would like to handle the errors with express like this instead.
return res.status(422).render('admin/edit-product', {flash message here.}
That's the way I do it with other errors like when the field is empty.
You can throw the error in your Multer custom storage (which is already being done with cb(err) ), and then catch it in a middleware for express.
const upload = multer({
storage: new customStorage({
destination: function(req, file, cb) {
cb(
null,
path.join(
__dirname,
'/images',
new Date().toISOString().replace(/:/g, '-') +
'-' +
file.originalname.replace(/\s+/g, '-')
)
);
}
}),
limits: { fileSize: 5000000 }
});
var uploadMiddleware = function(req, res, next){
var handler = upload.single('media'); //use whatever makes sense here
handler(req, res, function(err){
//send error response if Multer threw an error
if(err){
res.status(500).render('admin/edit-product', "flash message here.");
}
//move to the next middleware, or to the route after no error was found
next();
});
}
Then use the uploadMiddleware in your express route:
app.post('/route/edit', uploadMiddleware, function (req, res) {
//handle request and render normally
});

Multer is uploading file, but not populating req.files

I'm studying node.js for a school project and I can't figure out why my code won't work. Whenever I upload a form that contains text and a file, the req.body gets populated but the req.files doesn't
server.js
const multer = require('multer')
const bparser = require('body-parser')
app.use(bparser.urlencoded(settings.body_parser))
...
let multer_storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, path.join(__dirname, settings.multer.destination))
},
filename: (req, file, cb) => {
cb(null, Date.now() + path.extname(file.originalname))
}
})
let multer_options = {
storage: multer_storage,
fileFilter: (req, file, cb) => {
if (settings.multer.allowed_files.indexOf(file.mimetype) >= 0)
cb(null, true)
cb(null, false)
}
}
app.use(multer(multer_options).any())
app.use("*", (req, res, next) => {
if (!req.session.user)
if (req.cookies.user)
req.session.user = req.cookies.user
next()
})
for (let i = 0; i < settings.routes.length; i++) {
app.use('/', require("./core/routers/" + settings.routes[i]))
}
...
./core/routers/post.js
const router = require('express').Router()
...
router.post('/post/share/', (req, res) => {
let data = {
title: req.body.title,
user: req.session.user,
post: req.files[0].path,
tags: req.tags.split(" ")
}
post.create(data).then((result) => {
return result
})
})
I keep encountering a "TypeError: Cannot read property 'path' of undefined"
When you call cb(null, false) in your fileFilter method, you tell multer that it shouldn't process the file, but it will still enter your middleware with: req.files being undefined that's why you get that error.
If you don't want it to enter to your middleware if the file wasn't processed, then you should pass an error to the callback instead:
let multer_options = {
storage: multer_storage,
fileFilter: (req, file, cb) => {
if (settings.multer.allowed_files.indexOf(file.mimetype) >= 0)
return cb(null, true); // this return is missing
cb(new Error('Invalid file'));
}
}
In any case, you're missing a return statement before cb(null, true); otherwise you're calling twice the callback, once with true and the other once with false
To sum up, if you don't pass an Error to the fileFilter function, you should check for the presence of req.files in your middleware.
Or you can try the code below:
var tmp_path = req.file.path;

Custom Multer storage - Illegal operation when using Sharp

I'm using Multer together with Sharp to store images uploaded as part of an HTML form. I want to resize and transform the images before storing them on the disk and found this thread about how to do just that.
I thought I had set-up everything correctly, but when I try and upload an image I get:
Error: EISDIR: illegal operation on a directory, open 'C:\...\uploads'
Below is my code:
Routes.js:
var multer = require('multer');
var customStorage = require(path.join(__dirname, 'customStorage.js'));
var upload = multer({
storage: new customStorage({
destination: function (req, file, cb) {
cb(null, path.join(__dirname, 'uploads'));
},
filename: function (req, file, cb) {
cb(null, Date.now());
}
}),
limits: { fileSize: 5000000 }
});
...
app.use('/upload', upload.single('file'), (req, res) => { ... });
customStorage.js:
var fs = require('fs');
var sharp = require('sharp');
function getDestination (req, file, cb) {
cb(null, '/dev/null'); // >Implying I use loonix
};
function customStorage (opts) {
this.getDestination = (opts.destination || getDestination);
};
customStorage.prototype._handleFile = function _handleFile(req, file, cb) {
this.getDestination(req, file, function (err, path) {
if (err) return cb(err);
var outStream = fs.createWriteStream(path);
var transform = sharp().resize(200, 200).background('white').embed().jpeg();
file.stream.pipe(transform).pipe(outStream);
outStream.on('error', cb);
outStream.on('finish', function () {
cb(null, {
path: path,
size: outStream.bytesWritten
});
});
});
};
customStorage.prototype._removeFile = function _removeFile(req, file, cb) {
fs.unlink(file.path, cb);
};
module.exports = function (opts) {
return new customStorage(opts);
};
The error Error: EISDIR: illegal operation on a directory in this context indicates that you are setting Multer's destination to a directory when it should be the name of the destination file.
The destination is set in the line cb(null, path.join(__dirname, 'uploads')); in Routes.js. If you change this line to something like cb(null, path.join(__dirname, 'myDirectory\\mySubdirectory\\', myFilename + '.jpg')), it will work.

How to resize image size in nodejs using multer

Multer have already limit size property. This property only restrict the image. Not resize the image. My question is suppose image is greater than "limit size", how to resize that image ?
var storageOptions = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'useravatars/')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
var avatarUpload = multer({
storage: storageOptions,
limits: {
fileSize: 1000000
}
}).single("avatar");
It depends on whether you want to store the resized image as well.
In any case, you'll use a library to handle the resize operation. sharp is a very good option.
Resize in a route handler(after file is stored to disk):
sharp(req.file).resize(200, 200).toBuffer(function(err, buf) {
if (err) return next(err)
// Do whatever you want with `buf`
})
Other option would be creating your own storage engine, in this case you'll receive the file data, resize, then store to disk (copied from https://github.com/expressjs/multer/blob/master/StorageEngine.md):
var fs = require('fs')
function getDestination(req, file, cb) {
cb(null, '/dev/null')
}
function MyCustomStorage(opts) {
this.getDestination = (opts.destination || getDestination)
}
MyCustomStorage.prototype._handleFile = function _handleFile(req, file, cb) {
this.getDestination(req, file, function(err, path) {
if (err) return cb(err)
var outStream = fs.createWriteStream(path)
var resizer = sharp().resize(200, 200).png()
file.stream.pipe(resizer).pipe(outStream)
outStream.on('error', cb)
outStream.on('finish', function() {
cb(null, {
path: path,
size: outStream.bytesWritten
})
})
})
}
MyCustomStorage.prototype._removeFile = function _removeFile(req, file, cb) {
fs.unlink(file.path, cb)
}
module.exports = function(opts) {
return new MyCustomStorage(opts)
}
const path = require("path");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.join(__dirname, "/uploads"));
},
filename: function (req, file, cb) {
cb(null, uuid.v4() + `${path.extname(file.originalname)}`);
}
});
const limits = {
fields: 10,
fileSize: 500 * 1024,
files: 1,
};
const upload = multer({ storage, limits });
const baseUrl = "http://localhost:3000/files/";
router.post("/upload", upload.single("file"), async (ctx, next) => {
ctx.body = {
code: 1,
data: baseUrl + ctx.file.filename,
};
});

Resources