Multer limit images accepted - node.js

I am working with Multer and Multer S3 to allow users to upload images on a Node project. I am wanting the user to be able to upload 3 images regardless of their size but no more. If a user tries to select say 6 images I get this error page:
Error: Unexpected field
at makeError
How would I change my code below to not allow a user to select/upload more than 3 images and/or handle the error in a better way.
Partial Code:
router.post("/", upload.array('image', 3), function(req, res, next){
var filepath = undefined;
var filepath2 = undefined;
var filepath3 = undefined;
if(req.files[0]) {
filepath = req.files[0].key;
}
if(req.files[1]) {
filepath2 = req.files[1].key;
}
if(req.files[2]) {
filepath3 = req.files[2].key;
}.....
Update:
I added this and now I land on a "too many files" error page if the user tries to upload more than 3. Now I just have to figure out how to redirect if too many files.
var upload = multer({
limits : { files: 3 },

I played around with multer and here is something that I was able to get working.
Define your multer setup like this
var upload = multer().array('image', 3);
Then modify your function to this,
router.post("/", function(req, res, next){
upload(req, res, function (err) {
if (err) {
//redirect your user here ...
return;
}
//no multer error proceed with your normal operation ...
})
});

Related

Uploading image with MongoDB, Express.js and Node.js

The image is being saved in the database and in a folder for the uploads. However, when i try to display the image on another page, it can't seem to find it. This is my controllerimage on database.
exports.create = async (req, res, next) => {
try {
// eslint-disable-next-line prefer-const
let entity = new Product(req.body);
entity.image = `uploads/${req.file.filename}`;
entity.userId = req.user._id;
const saved = await entity.save();
res.status(httpStatus.CREATED);
res.json(saved);
} catch (error) {
next(error);
}
};
I tried to change the route to "localhost:3000/uploads/${req.file.filename}", "localhost:3000/v1/${req.file.filename}" and other variations of it, but it doesn't seem to work anyway. It's just not finding the image.

How to resize image with sharp then upload with multer in nodejs

I'm developing a feature to allow user upload image to mongodb with nodejs :
My Problem :
Get image file from user's request and do 2 task: store current image to mongodb with collection name "Origin_image" for example and resize current image and store to mongodb with collection name "Thumbnail_image"
My solution so far:
I just only store success original image by using multer-gridfs-storage and multer like code below
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
let storageFS = new GridFsStorage({
db: app.get("mongodb"),
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = file.originalname;
const fileInfo = {
filename: filename,
bucketName: 'images'
};
resolve(fileInfo);
});
});
}
});
var upload = multer({ storage: storageFS }).single('image');
exports.uploadImage = async function (req, res) {
try {
upload(req, res, function (err) {
if (err) {
return res.send(err)
}
res.json({
status: true,
filePath: req.file.originalname
});
});
} catch (error) {
res.send(error);
}
}
Does anyone have any idea to solve my problem? thanks !
If you are using Angular on your frontend, let the end user handle the image resizing so that your server does not have to deal with the overhead. I am currently using ng2-img-max to resize images. You can initiate the resize as on file change.
I also wanted to have thumbnails and then the original, but this caused a huge issue in performance when resizing both and then again how to link them as GridFs stores them before you can do anything with them and all you have left is the response. So save yourself some time. Only resize once, to your limited size for the user and then for displaying thumbnail images, use sharp with custom query params to display the size you want.
Good luck and happy coding.

Storing images and calling them from MongoDB

I have looked around online and gotten only a few methods to work in test code but never on my actual code. I am trying to let the user upload an image in .jpg and then I want to call on it later to be displayed. Here is what I got so far.
fs = require("fs");
multer= require("multer");
app.use(multer({ dest: "./uploads/:id",
rename: function (fieldname, filename) {
return filename;
},
}));
Then to pull the file from the form I am using:
image: req.body.image
As for the code to put it into the DB I am not sure, this is what I came up with but not too sure on if it will work or not. I also am clueless where to put it into my larger route I already have for the rest of the form.
Here is the small code:
app.post("/api/photo",function(req,res){
var Startup = new Startup();
Startup.img.data = fs.readFileSync(req.files.userPhoto.path);
Startup.img.contentType = "image/jpg";
Startup.save();
});
And here is the working (other than image) route code for the rest of the form.
// CREATE add new startup to database
app.post("/startup-submit", function(req, res) {
// Get data from form
I create the variables here, removed as too much code and code for the image draw is above.
//Pass data through | Write better explaination later
var newStartup = {about_startup: about_startup, social_media: social_media, about_founder: about_founder};
Startup.create(newStartup, function(err, newlyCreatedStartup){
if(err){
console.log(err);
} else {
// Redirect back to show all page
res.redirect("/startups");
}
});
});
I know the route paths on the small and large code don't line up but i was using that for testing.
How can I mesh and fix this code so that it will allow me to upload an image/file to my DB?
Then how would I call it to be an src using EJS. Would it just be "> This is the best I have came up with. I am sure it is far from right though.
Follow the documentation.
(1) dest is where you store your files. ./uploads/:id does not look like a valid destination.
(2) Where did you get rename option?
(3) Don't store the actual image in the database. Just store the filename.
It should look more like
var fs = require("fs"),
multer = require("multer");
var upload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
// here is where you would add any extra logic to create directories
// for where you will upload your file
cb(null, './uploads')
},
filename: function (req, file, cb) {
// here is where you would add any extra logic to rename your file
cb(null, file.fieldname);
}
})
});
app.post("/api/photo", upload.single('fieldname'), function (req, res, next) {
// at this point, the file should have been uploaded
// you can get info about your file in req.file
console.log(req.file);
var startup = new Startup();
startup.img.data = req.file.filename;
startup.img.contentType = req.file.mimetype;
startup.save(function (err) {
if (err) return next(err);
// send response
});
});
You might find fs-extra useful, particulary fs.mkdirs() if you want to create directories e.g. /this/path/does/not/exist.

Parse multipart form data and then upload a file with Multer (Node module)

I'm having some troubles trying to figure out how to prevent Multer from uploading a file if an entry has already been added to the database.
In my scenario, I have a multipart form where you can add beers specifying an id. It has 2 inputs, one with a text input (id) and another one with a file input (beerImage).
What I'd like to do is, before uploading a file, check if it already exist in the database and, if not, upload the file. Currently, my code is uploading the file first and then checking if it exists, and that's a bad thing!
That's my code:
var express = require('express');
var router = express.Router();
var multer = require('multer');
var database = require('../services/database');
var upload = multer({ dest: 'uploads/' });
var cpUpload = upload.fields([{ name: 'beerImage', maxCount: 1 } ]);
router.route('/beers')
.post(function (req, res, next) {
// I'd like to use req.body.id in the query here, but it doesn't exist yet!
cpUpload(req, res, function (err) {
if (err) {
return next(new Error('Error uploading file: ' + err.message + ' (' + err.field + ')'));
} else {
database.getConnection().done(function (conn) {
conn.query('SELECT COUNT(*) AS beersCount FROM beers WHERE id=?', req.body.id, function (err, rows, fields) {
if (err) {
conn.release();
return next(err);
}
if (rows[0].beersCount > 0) {
conn.release();
return next(new Error('Beer "' + req.body.id + '" already exists!'));
} else {
delete req.body.beerImage;
conn.query('INSERT INTO beers SET ?', req.body, function (err, rows, fields) {
conn.release();
if (err) {
return next(err);
}
res.json({ message: 'Beer "' + req.body.id + '" added!' });
});
}
});
});
}
});
});
module.exports = router;
I can't find a way to first "parse" the multipart form data (to be able to do the query and check if it exists using req.body.id) and then decide if I want to upload the file or not. It seems that "cpUpload" does both things at the same time!
Any idea of how to get "req.body.id" first (to do the query) and then decide if I want to upload the file?
I realized there is a function called "fileFilter" on Multer where you can control which files are accepted. Here I've been able to do the desired query to check if the entry exists.
You have to upload the file before testing if it exists. And then save it or drop it
This is how I achieved file fields and text fields validation of multipart/formdata before uploading to the cloud. My filterFile function inside multer looks like this:
Note any data sent using postman's formdata is considered as multipart/formdata. You have to use multer or other similar library in order to parse formdata.
For x-www-form-urlencoded and raw data from postman does not require multer for parsing. You can parse these data either using body-parser or express built-in middlewares express.json() and express.urlencoded({ extended: false, })
filterFile = async (req, file, cb) => {
// if mimetypes are expected mimetypes
const allowedMimeTypes = ["image/jpeg", "image/jpg", "image/png"];
if (allowedMimeTypes.includes(file.mimetype)) {
// then validate formdata fields
// reject the request with error if any validation fails
try {
let { highlights, specifications, ...rest } = req.body;
// deserializing string into respective javascript object(here into an array datatype) to facilate validation
highlights = JSON.parse(req.body.highlights);
specifications = JSON.parse(req.body.specifications);
// constructing deserialized formdata
const formdata = { highlights, specifications, ...rest };
// using Joi for validation
let skuAndProductResult = await skuAndProductSchema.validateAsync(
formdata
);
if (skuAndProductResult) {
// accept the fields and proceed to the next middleware
cb(null, true);
return;
}
// reject the request with error
cb(true, false);
return;
} catch (err) {
// reject the request with error
cb(err, false);
}
} else {
// To reject this file pass `false`
cb(`${file.mimetype} is not supported`, false);
}
};

lwip.open doesn't work after mongoose findOne

So I have mongoose, multer and lwip (they are required from the top part).
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var jwt = require('jsonwebtoken');
var Users = require('../models/users.js');
var multer = require('multer');
var mime = require('mime');
var lwip = require('lwip');
If I comment the Users.findOne part, the image is cropped as I want it to be cropped. But if I uncomment it the lwip part stops working, though no errors are thrown. It just doesn't enter lwip.open().
router.post('/image', upload.single('file'), function (req, res) {
//This part works always.
Users.findOne({userid: req.body.userid}, function (err, user) {
var imgpath = req.file.path.split("public\\")[1];
user.user_photos.push(imgpath);
user.save(function (err) {
if (err)
console.log('error');
else
console.log('success');
});
});
//If I remove the top part, this part will work too.
lwip.open(req.file.path, function(err, image){
image.batch()
.crop(200,200)
.writeFile(req.file.path, function(err) {
if (err)
console.log('error');
else
console.log('success');
});
});
});
You might need my multer config too, so here it is:
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/')
},
filename: function (req, file, cb) {
var extension;
if (mime.extension(file.mimetype) == "jpeg") {
extension = "jpg";
}
else {
extension = mime.extension(file.mimetype);
}
cb(null, file.fieldname + '-' + Date.now() + '.' + extension);
}
});
Can you help me to figure out what the problem is here?
They're both async functions. How can you guarantee both are done before the function exits? I recommend using a promise library like Bluebird. With it you can run multiple async functions at the same time and specify what happens when they both return.
Don't forget to 'promisify' any libraries that are used that you want to treat as promises. You're code will look something like:
my route('blah', function (){
return Promise.all([ myfunct1, myfunct2], (retval) => { return {f1val: retval[1], f2val: retval[2]}})
I know some asshat is going to come along and take my answer and write out the code for you so that all you have to do is copy paste it, but I really do hope that you take the time to learn WHY and HOW it works if you do not already know.

Resources