Storing images and calling them from MongoDB - node.js

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.

Related

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.

Multer limit images accepted

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

Multer and ImageMagick with Express.js: upload, resize, reorient

I have an Express application that gets an image from a user via a form. There are several things I need to do with the image, and as it gets more complex, I'm not sure how to handle it. It is a message board post where there are some required text fields and an optional image upload. I need to:
Find the orientation of the image from EXIF data and reorient it if needed
Save a copy of the original image to the server (done)
Create a thumbnail of the image and save it to the server (done)
Save the record to the database, whether or not there's an uploaded image (done)
I'm concerned about the order in which I'm doing things, wondering if there's a more efficient way. I know I can call upload inside the route instead of passing it in, but I'd like to not repeat myself when I save the record to the database, since I need to save it whether there's an image or not.
I have code that's working for the final 3 steps, but am open to suggestions on how to improve it. For the first step, I'm stumped at how to go about getting the orientation of the original and rotating it if needed. Is this something I need to do client-side instead? And how do I work it into the existing code?
Here's the code:
Setup
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads');
},
filename: function (req, file, cb) {
var fileExt = file.mimetype.split('/')[1];
if (fileExt == 'jpeg'){ fileExt = 'jpg';}
cb(null, req.user.username + '-' + Date.now() + '.' + fileExt);
}
})
var restrictImgType = function(req, file, cb) {
var allowedTypes = ['image/jpeg','image/gif','image/png'];
if (allowedTypes.indexOf(req.file.mimetype) !== -1){
// To accept the file pass `true`
cb(null, true);
} else {
// To reject this file pass `false`
cb(null, false);
//cb(new Error('File type not allowed'));// How to pass an error?
}
};
var upload = multer({ storage: storage, limits: {fileSize:3000000, fileFilter:restrictImgType} });
In Route
router.post('/new',upload.single('photo'),function(req,res){
var photo = null;
var allowedTypes = ['image/jpeg','image/gif','image/png'];
if (req.file){
photo = '/uploads/' + req.file.filename;
// save thumbnail -- should this part go elsewhere?
im.crop({
srcPath: './public/uploads/'+ req.file.filename,
dstPath: './public/uploads/thumbs/100x100/'+ req.file.filename,
width: 100,
height: 100
}, function(err, stdout, stderr){
if (err) throw err;
console.log('100x100 thumbnail created');
});
// I can get orientation here,
// but the image has already been saved
im.readMetadata('./public/uploads/'+ req.file.filename, function(err, metadata){
if (err) throw err;
console.log("exif orientation: " + metadata.exif.orientation);
});
}
// Save it
new Post({
username: req.user.username,
title: req.body.title,
body: req.body.messagebody,
photo: photo
}).save(function(err){
if (err){ console.log(err); }
res.redirect('/messageboard');
});
});
Thanks for your help

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.

How to retrieve image file from Mongo using Sails.JS + GridFS?

I'm currently building web using Sails.js and got stuck in retrieving image file from GridFS. I have successfully uploading the file using skipper-gridfs to my mongo gridfs. I have no idea to display the file in the correct way (I'm new in Sails.js and Node system)
Here is my code for retrieving image file from gridfs looks like in FileController.js (I'm using gridfs-stream):
show: function (req, res, next) {
var mongo = require('mongodb');
var Grid = require('gridfs-stream');
var buffer="";
// create or use an existing mongodb-native db instance
var db = new mongo.Db('testDb', new mongo.Server("192.168.0.2", 27017), {safe:true});
var gfs = Grid(db, mongo);
// streaming from gridfs
var readstream = gfs.createReadStream({
filename: 'e1ecfb02-e095-4e2f.png'
});
//check if file exist
gfs.exist({
filename: 'e1ecfb02-e095-4e2f.png'
}, function (err, found) {
if (err) return handleError(err);
found ? console.log('File exists') : console.log('File does not exist');
});
//buffer data
readstream.on("data", function (chunk) {
buffer += chunk;
console.log("adsf", chunk);
});
// dump contents to console when complete
readstream.on("end", function () {
console.log("contents of file:\n\n", buffer);
});
}
When I ran it, the console showed nothing.
There is no error either.
How should I fix this?
Additional Question:
Is it better & easier to store/read file to/from local disk instead of using gridfs?
Am I correct in choosing gridfs-stream to retrieve the file form gridfs?
In the skipper-gridfs codes and there's a 'read' method that accept fd value and returns the required file corresponding to that value. So, you just have to pull that file from mongo by that method and send as a response. It should work file.
download: function (req, res) {
var blobAdapter = require('skipper-gridfs')({
uri: 'mongodb://localhost:27017/mydbname.images'
});
var fd = req.param('fd'); // value of fd comes here from get request
blobAdapter.read(fd, function(error , file) {
if(error) {
res.json(error);
} else {
res.contentType('image/png');
res.send(new Buffer(file));
}
});
}
I hope it helps :)
Additional Questions:
Yes, using gridfs is better both in performance and efficiency. And normally mongodb has a limitation of 16MB probably for binary files, but using gridfs you can store any size file, it breaks them in chunks and stores them.
Retrieving has been shown above.
You can now use skipper-gridfs in sails to manage uploads/downloads.
var blobAdapter = require('skipper-gridfs')({uri: 'mongodb://jimmy#j1mtr0n1xx#mongo.jimmy.com:27017/coolapp.avatar_uploads' });
Upload:
req.file('avatar')
.upload(blobAdapter().receive(), function whenDone(err, uploadedFiles) {
if (err) return res.negotiate(err);
else return res.ok({
files: uploadedFiles,
textParams: req.params.all()
});
});
Download
blobAdapter.read(filename, callback);
Bear in mind the file name will change once you upload it to mongo, you have to use the file name returned in the first response.

Resources