How to handle erros from multer custom storage? - node.js

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
});

Related

How to catch errors in multer

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.
})
})

express body-parser and multer value receiving issue?

I am giving post request /product/create with some value and an image.
if I console every value before
upload(req, res, (err) => {})
it is showing properly with out image info.
if I receive the value after upload(req, res, (err) => {})
No value is showing.
Full post request code:
app.post('/product/create', (req, res) => {
let filename;
upload(req, res, (err) => {
if(err){
res.render('index', {
msg: err
});
} else {
if(req.file == undefined){
res.render('index', {
msg: 'Error: No File Selected!'
});
} else {
res.render('index', {
msg: 'File Uploaded!',
filename = req.file.filename;
});
}
}
});
const product = {
title : req.body.title,
desc : req.body.desc,
image : filename,
}
});
configuring Multer:
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function(req, file, cb){
cb(null,file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).single('myImage');
function checkFileType(file, cb){
const filetypes = /jpeg|jpg|png|gif/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: Images Only!');
}
}
Multer does not support 'req.file.filename' outside upload function. As filename, originalname, fieldname etc is inbuild API of multer. It is limited to upload function only.
Now, if you are trying to upload product values inside database then you have to create an insert function inside multer upload function only.

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,
};
});

Rename uploaded images using MULTER

I am uploading the images using multer. They all are given random names (dec93b9f333c7a731723b06ce73c0bbc.jpg), which is very bad for SEO... Can you guys help me out, how to save the images with the pattern: 'fixed-name'+'random-name'.extension. Then at least part of the file would be readable for the google. Thanks!
app.set('images', '/var/www/images');
app.use(app.get('images'), express.static(app.get('images')));
var multerForImage = multer({
dest: app.get('images'),
onParseStart: function (file) {
console.log("Started parsing file stream", file);
},
onFileUploadStart: function (file) {
console.log('File recieved: ', file.originalname);
},
onFileUploadComplete: function (file, req, res) {
console.log("File upload complete");
var path = app.get('images') + "/" + file.name;
var user = req.session.user;
res.json({
success: true,
data: path
});
},
onFileUploadData: function (file, data, req, res) {
console.log('Data recieved for file upload');
},
onParseEnd: function (req, next) {
console.log("Parsing data end for file upload");
}
});
You can use the storage configuration.
app.set('images', '/var/www/images');
app.use(app.get('images'), express.static(app.get('images')));
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, app.get('images'))
},
filename: function (req, file, cb) {
const randomPart = uuidV4(); // use whatever random you want.
const extension = file.mimetype.split('/')[1];
cb(null, 'fixed-name'+ randomPart + `.${extension}`)
}
})
var multerForImage = multer({
storage: storage,
...

Resources