How and When to generate thumbnail in node.js? - node.js

So here is how my app handle image uploading and generate thumbnail
router.post('/upload', saveupload, generateThumb, saveDB)
function generateThumb (req, res, next){
var filePath = req.files.file.path;
var fileDir = req.files.file.dir;
var filename = req.files.file.name;
async.series({
noProfile:function(callback){
gm(filePath)
.noProfile()
.write(filePath, function(err){
callback(err);
})
},
large:function(callback){
var thumb_width = config.thumb.lg.width;
var thumb_height = config.thumb.lg.height;
var quality = config.thumb.lg.quality;
var thumbName = filename.split('.')[0]+'_thumb_'+thumb_width+'x'+thumb_height+'.'+filename.split('.')[1];
var thumbPath = path.join(fileDir, thumbName);
gm(filePath)
.noProfile()
.resize(thumb_width, thumb_height)
.quality(quality)
.write(thumbPath, function(err){
callback(err)
})
},
middle:function(callback){
var thumb_width = config.thumb.md.width;
var thumb_height = config.thumb.md.height;
var quality = config.thumb.md.quality;
var thumbName = filename.split('.')[0]+'_thumb_'+thumb_width+'x'+thumb_height+'.'+filename.split('.')[1];
var thumbPath = path.join(fileDir, thumbName);
gm(filePath)
.noProfile()
.resize(thumb_width, thumb_height)
.quality(quality)
.write(thumbPath, function(err){
callback(err)
})
},
small:function(callback){
var thumb_width = config.thumb.sm.width;
var thumb_height = config.thumb.sm.height;
var quality = config.thumb.sm.quality;
var thumbName = filename.split('.')[0]+'_thumb_'+thumb_width+'x'+thumb_height+'.'+filename.split('.')[1];
var thumbPath = path.join(fileDir, thumbName);
gm(filePath)
.noProfile()
.resize(thumb_width, thumb_height)
.quality(quality)
.write(thumbPath, function(err){
callback(err)
})
}},
function(err, obj){
if(err){
return next(err)
}
next()
})
}
the code work well, util now I find some problem:
1:generate thumbnail this way slows the uploading speed
for every picture user uploaded, I need to generate 3 thumbnails , large, middle, small, I first save the uploaded picture to disk then pick up the picture in gm to crop metadata and generate thumbnail, this method really slow down the uploading speed, new upload can not start util the previous thumbnail is generated. what I want is keep the thumbnail generation out of the request route, so I can maintain max upload speed. any idea how?
2:Is there a better way to batch generate thumbnail
As you can see, I need 3 thumbnail, I did not see any document of gm on how to batch generate thumbnail, but I think there has to be a better way.

Related

How to detect more than 10 faces in the google vision apis

Hi i am new to google vision apis. I want to detect the faces on the Image ,i am using the node.js. the local image containing more than 10 faces. but vision api returning only 10 faces Detection. Is there any way to detect all the faces using this Vision api. please refer vision node api.
and you can take this image as ref
Here is my code
function findFaceontheImage(req, res, next) {
var vision = Vision();
var inputfile = 'NASA_Astronaut_Group_15.jpg';
var outputFile = 'out.png';
vision.faceDetection({source: {filename: inputfile}})
.then(function (results) {
const faces = results[0].faceAnnotations;
console.log('Faces:');
req.body['faces']=results;
var numFaces = faces.length;
console.log('Found ' + numFaces + (numFaces === 1 ? ' face' : ' faces'));
highlightFaces(inputfile, faces, outputFile, Canvas, function (err) {
if (err) {
next()
}
console.log("Finished!");
next()
});
})
.catch(function (err) {
console.error('ERROR:', err);
});
}
function highlightFaces(inputFile, faces, outputFile, Canvas, callback) {
fs.readFile(inputFile, function (err, image) {
if (err) {
return callback(err);
}
var Image = Canvas.Image;
// Open the original image into a canvas
var img = new Image();
img.src = image;
var canvas = new Canvas(img.width, img.height);
var context = canvas.getContext("2d");
context.drawImage(img, 0, 0, img.width, img.height);
// Now draw boxes around all the faces
context.strokeStyle = "rgba(0,255,0,0.8)";
context.lineWidth = "5";
faces.forEach(function (face) {
context.beginPath();
var origX = 0;
var origY = 0;
face.boundingPoly.vertices.forEach(function (bounds, i) {
if (i === 0) {
origX = bounds.x;
origY = bounds.y;
}
context.lineTo(bounds.x, bounds.y);
});
context.lineTo(origX, origY);
context.stroke();
});
// Write the result to a file
console.log("Writing to file " + outputFile);
var writeStream = fs.createWriteStream(outputFile);
var pngStream = canvas.pngStream();
pngStream.on("data", function (chunk) {
writeStream.write(chunk);
});
pngStream.on("error", console.log);
pngStream.on("end", callback);
});
}
In case there're other people who's still struggling on this topic.
With the Node.js Client Library, you can pass the ImprovedRequest object to the client.faceDetection(..) method instead of using the filepath or imageuri.
For example, in my case, I want the api to process an image in my GCS. So, instead of placing the imageuri as string. I'd do something like below.
import { protos } from '#google-cloud/vision';
// BEFORE
const [result] = await CLIENT.faceDetection(`gs://${bucketName}/${filePath}`);
// AFTER
const [result] = await CLIENT.faceDetection({
image: {
source: { imageUri: `gs://${bucketName}/${filePath}` }
},
features: [
{
maxResults: 100,
type: protos.google.cloud.vision.v1.Feature.Type.FACE_DETECTION,
},
],
});
Just in case noone will come up with solution that would force API to return more results, a pseudocode:
def process(image)
faces = process image
return faces if faces.size < 10
split image into two a bit overlapping half1 and half2
# we do overlapping because splitting may split a face
a = process(half1)
b = process(half2)
return a + b - intersection(a + b)
The intersection function should throw out those images that are on the same (taking in mind the possible +/-few pixel errors) coordinates plus the shift that we had between half1 and half2 withing the image.

I am trying to send array of images but I am unable to get data in server using nodejs

Here is my plunker link
https://plnkr.co/edit/0I0bbymoekwpyIXSOFJu?p=catalogue
I want to know how upload array of images.
For the single file, it's working, for multiple files I want to know what I need to write in node js.
My code
router.route('/events')
.post(upload.single('event_image'),function(req,res){
console.log('**Inside create events**');
console.log(":req.body",req.body);
console.log(":req.fileeeeeee",req.body);
var event = new Event();
event.name = req.body.name;
event.description = req.body.description;
event.location = req.body.location;
event.startdate = req.body.startdate;
event.enddate = req.body.enddate;
event.tagline = req.body.tagline;
event.password = req.body.password;
event.passcode = getEventPasscode();
event.uid = req.body.user_id;
})
Use upload.array('event_images', 20) (20 is maxCount) instead of upload.single('event_image')
EDIT:
Here is a code snippet that shows working route in node.js
router.post('/upload',
function(req, res) {
upload.array('event_images', 20),
function(req, res) {
_.each(req.files, file => {
console.log(file.path);
console.log(file.name);
});
res.json();
}
);

Downloading an audio file with Express API and ytdl

I'm trying to download a Youtube video audio using the ytdl-core module (https://github.com/fent/node-ytdl-core).
I wrote an API using Express which lets me download an audio by its URL:
app.get('/api/downloadYoutubeVideo', function (req, res) {
res.set('Content-Type', 'audio/mpeg');
var videoUrl = req.query.videoUrl;
var videoName;
ytdl.getInfo(videoUrl, function(err, info){
videoName = info.title.replace('|','').toString('ascii');
res.set('Content-Disposition', 'attachment; filename=' + videoName + '.mp3');
});
var videoWritableStream = fs.createWriteStream('C:\\test' + '\\' + videoName); // some path on my computer (exists!)
var videoReadableStream = ytdl(videoUrl, { filter: 'audioonly'});
var stream = videoReadableStream.pipe(videoWritableStream);
});
The problem is that when I call this API I get a 504 error from my server.
I want to be able to save this downloaded audio on my local disk.
Help would be appreciated. Thank you
Well for some reason the videoName was undefined so it messed up my function...
Here is the correct code after a few changes and adding the destination path as query variable.
app.get('/api/downloadYoutubeVideo', function (req, res) {
var videoUrl = req.query.videoUrl;
var destDir = req.query.destDir;
var videoReadableStream = ytdl(videoUrl, { filter: 'audioonly'});
ytdl.getInfo(videoUrl, function(err, info){
var videoName = info.title.replace('|','').toString('ascii');
var videoWritableStream = fs.createWriteStream(destDir + '\\' + videoName + '.mp3');
var stream = videoReadableStream.pipe(videoWritableStream);
stream.on('finish', function() {
res.writeHead(204);
res.end();
});
});
});

Store an Image from registration page using NodeJS

I am using ExpressJS and NodeJS .
I build a registration page which require from the user to upload his image.
the code in Jade
.form-container
h1 Create a User
form(name="adduser",method="post",action="/adduser")
.col-xs-13
p Username
input.form-control(type="text", name="username")
.col-xs-13
p Email
input.form-control(type="text", name="useremail")
.col-xs-13
p Upload an Image
input.form-control(type='file', name ="pic", accept='image/*')
.col-xs-13
p Password
input#password.form-control(type='text', name='pass', required='')
.col-xs-13
p Confirm Password
input#confirm_password.form-control(type='text', required='' , onkeyup='checkPass(); return false;')
span#confirmMessage.confirmMessage
br
.form-group
button#myBtn.btn.btn-primary(type="submit") submit
i also have a script down there but its doesnt matter right now.
the thing is i want to save the image in my Express Project directory but i am not sure how to do that. here is the code which store the other information in Mongodb DB , i just need to save the image now.
/* POST to Add User Service */
router.post('/adduser', function(req, res) {
// Set our internal DB variable
var db = req.db;
// Get our form values. These rely on the "name" attributes
var userName = req.body.username;
var userEmail = req.body.useremail;
var userPass = req.body.pass;
var collection = db.get('users');
// Submit to the DB
collection.insert({
"username" : userName,
"email" : userEmail,
"password" : userPass
}, function (err, doc) {
if (err) {
res.send("There was a problem adding the information to the database.");
}
else {
res.redirect("userlist");
}
});
});
This might help you:
var express = require('express'),
app = express(),
formidable = require('formidable'),
util = require('util'),
fs = require('fs-extra'),
bodyparser=require('body-parser'),
qt = require('quickthumb'),
path = require('path');
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/db');
var Images = require('./model.js');
app.use(qt.static(__dirname + '/'));
app.use(bodyparser());
app.set('view engine','ejs');
app.post('/upload',function (req, res){
var form = new formidable.IncomingForm();
var userObj = [];
form.parse(req, function(err, fields, files) {
});
form.on('field',function(name,value){
// you will get the all the input field values here...
console.log(name,value);//name gives input field name and value gives input value
userObj.push({name:value});
});
form.on('end', function(fields, files) {
//You can save the data into collection from the array
//Images is my model
var img = new Images();
var temp_path = this.openedFiles[x].path;
/* The file name of the uploaded file */
var file_name = this.openedFiles[x].name;
//console.log('file '+file_name);
img.size = this.openedFiles[x].size;
img.type = this.openedFiles[x].type;
/* Location where we want to copy the uploaded file */
var new_location = 'uploads/';
//to copy the file into a folder
fs.copy(temp_path, new_location + file_name, function(err) {
if (err) {
console.log(err);
}
});//fscopy
});//form end
res.send('Done!!');
});//post
app.listen(3000);
console.log('started server');
For express use this.
var formidable = require('formidable');
npm i formidable
router.post("/one", (req, res) => {
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
console.log(fields);
console.log(files);
res.send(files);
});
});

Issue with delivering a resized image on request with node

I am using sailsjs, the website is sailsjs.org.
Im tring to create a controller function so that when an image is requested it can be resized and cached the first time a visitor asks for an image, then retrieved from cache the next time a visitor requests it.
I have setup a route to an asset controller:
'get /image/:width/:height/:image': {
controller: 'AssetController',
action: 'image'
},
I have setup a policy to intercept requests for an image from the above route and resize / save the file into a cache:
var fs = require('fs'),
im = require('imagemagick');
module.exports = function imageCacheResize(req, res, next){
var width = req.param('width');
var height = req.param('height');
var file = req.param('image');
if(width && height){
//read from cache
dir = 'assets/images/uploads/cache/'+width+'X'+height+'/';
filename = dir+file;
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
if(!fs.exists(filename)){
//write to file if not exist
var originalFile = 'assets/images/uploads/'+file;
im.resize({
srcPath: originalFile,
dstPath: filename,
width: width
}, function(err, stdout, stderr){
if (err) throw err;
});
}
}
next();
};
I have also got an action/function setup for the controller to handle returning the resized image:
image: function(req, res){
var file = req.param('image');
var filename = 'assets/images/uploads/'+file;
if((typeof req.param('width') != 'undefined')
&&(typeof req.param('height') != 'undefined'))
{
var width = req.param('width');
var height = req.param('height');
}
if(typeof req.param('size') != 'undefined'){
var size = req.param('size');
}
if(width && height){
//read from cache
dir = 'assets/images/uploads/cache/'+width+'X'+height+'/';
file = dir+file;
}else{
file = 'assets/images/uploads/'+file;
}
console.log(file);
res.sendfile(file);
}
All of the code works correctly and creates then saves the resized image, however it does not return the image on the first request but it does on the second.
So it turns out I had not placed the next() commands in the correct place. Seems like whilst waiting for an image resize the code continues and hits the final next().
I have change the image resize policy to this:
var fs = require('fs'),
im = require('imagemagick');
module.exports = function imageCacheResize(req, res, next){
var width = req.param('width');
var height = req.param('height');
var file = req.param('image');
if(width && height){
//read from cache
dir = 'assets/images/uploads/cache/'+width+'X'+height+'/';
filename = dir+file;
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
if(!fs.exists(filename)){
//write to file if not exist
var originalFile = 'assets/images/uploads/'+file;
im.resize({
srcPath: originalFile,
dstPath: filename,
width: width
}, function(err, stdout, stderr){
if (err) throw err;
next();
});
}else{
next();
}
}else{
next();
}
};

Resources