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
Related
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.
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.
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.
I am trying to write an import script in Nodejs that pulls data from the web and formats it and then sends it to my API.
Part of that includes pulling artist data from LastFM, fetching the images for each artist and sending them off to my API to resize and save.
The import script is just ran in terminal.
The part of the import script that is responsible for pulling the images down and sending off to my API looks like:
_.forEach(artist.images, function(image){
console.log('uploading image to server ' + image.url)
request.get(image.url)
.pipe(request.post('http://MyAPI/files/upload', function(err, files){
if (err) {
console.log(err);
}
console.log('back from upload');
console.log(files);
}));
});
And the files.upload action looks like:
upload: function(req, res){
console.log('saving image upload');
console.log(req.file('image'));
res.setTimeout(0);
var sizes = [
['avatar', '280x210'],
['medium', '640x640'],
['large', '1024x768'],
['square', '100x100'],
['smallsquare', '50x50'],
['avatarsquare', '32x32']
];
//resize to the set dimensions
//for each dimension - save the output to gridfs
_.forEach(sizes, function(bucket){
var width = bucket[1, 0], height = bucket[1, 2];
// Let's create a custom receiver
var receiver = new Writable({objectMode: true});
receiver._write = function(file, enc, cb) {
gm(file).resize(width, height).upload({
adapter: require('skipper-gridfs'),
uri: 'mongodb://localhost:27017/sonatribe.' + bucket[0]
}, function (err, uploadedFiles) {
if (err){
return res.serverError(err);
}
else{
return res.json({
files: uploadedFiles,
textParams: req.params.all()
});
}
});
cb();
};
/* req.file('image').upload(receiver, function(err, files){
if(err) console.log(err);
console.log('returning files');
return files;
});*/
});
}
However, console.log(req.file('image')); is not what I'd hope - probably because this code is expecting the image to be uploaded as part of a multi-part form upload with a field named image - which it is not...
I'm trying to figure out how the file will end up inside my action but my google foo is completely out of action today and I'm fairly (very) new to Node.
Anyone able to offer some pointers?
I've found few articles explaining the process but most of them are not up do date.
How do you handle image upload in node.js?
Im using multer and it works perfectly. It stores your image locally. You can also send it to mongodb if you want. This is how i am doing it.
var multer = require('multer');
var done = false;
//define the model you are working with*
var Slides = require('./models/work');
app.use(multer({
dest: './public/img',
rename: function (fieldname, filename) {
return filename+Date.now();
},
onFileUploadStart: function (file) {
console.log(file.originalname + ' is starting ...')
},
onFileUploadComplete: function (file) {
console.log(file.fieldname + ' uploaded to ' + file.path);
done = true;
var id= file.fieldname;
var str = file.path;
var image = str.replace('public', '');
var slidegegevens = {
"id": id,
"img": image
};
var s = new Slides(slidegegevens);
s.save(function (err, slidegegevens) {
console.log(err);
console.log('slidegegevens: ' + slidegegevens);
});
}
}));
I use busboy middleware in express to parse out images in a multipart/form-data request and it works pretty nice.
My code looks something like:
const busboy = require('connect-busboy');
//...
app.use(busboy());
app.use(function parseUploadMW(req,res,next){
req.busboy.on('file', function onFile(fieldname, file, filename, encoding, mimetype) {
file.fileRead = [];
file.on('data', function onData(chunk) {
this.fileRead.push(chunk);
});
file.on('error', function onError(err) {
console.log('Error while buffering the stream: ', err);
//handle error
});
file.on('end', function onEnd() {
var finalBuffer = Buffer.concat(this.fileRead);
req.files = req.files||{}
req.files[fieldname] = {
buffer: finalBuffer,
size: finalBuffer.length,
filename: filename,
mimetype: mimetype.toLowerCase()
};
});
});
req.busboy.on('finish', function onFinish() {
next()
});
req.pipe(req.busboy);
})
Then files will be in the req object for you at req.files in your express routes.
This technique works fine for small images. If you are doing some hardcore uploading, you may want to consider streaming the files (to save memory) to their destination - like s3 or similar - which can also be achieved with busboy
Another package that is popular and also decent is: https://github.com/andrewrk/node-multiparty.
I think is better use formidable to handle incoming images.