I've been trying to use gm with Bluebird like this:
var gm = require('gm');
var bluebird = require('bluebird');
gm = bluebird.promisifyAll(gm);
But then, when I try something like this:
gm(req.file.buffer)
.crop(tWidth, tHeight, tWidth/2, tHeight/2)
.gravity('Center')
.toBuffer()
.then(...)
I get this error:
gm().toBuffer() expects a callback.
If I only promisify the buffer method:
bluebird.promisify(gm(req.file.buffer)
.crop(tWidth, tHeight, tWidth/2, tHeight/2)
.gravity('Center')
.toBuffer)()
.then(buff => {...})
I get this error:
TypeError: this.stream is not a function 6:36:21 AM web.1 | at toBuffer (/Users/danielrvt/IdeaProjects/twe-backend/node_modules/gm/lib/command.js:162:17)
If I don't use promises, it works just fine.
That's not really how you're supposed to use the gm
module as it is designed to provide a factory gm()
and a number of cascadeable mutator functions like .resize()
.
Bluebird's .promisifyAll()
only works on functions that accept a callback, which the majority of gm
's functions do not.
If you wish to use gm
with promises, you'll need to "promisify" it yourself, by wrapping your call as
function mutateAndSave() {
return new Promise( function(resolve,reject) {
try {
gm(image)
.doSomething()
.write(outputPath, function(err) {
if(err) {
throw(err);
}
resolve();
});
}
catch (err) {
reject(err);
}
});
}
After which, you can
mutateAndSave()
.then(...)
.catch(...);
Here are two ways to do what you want, but...
You'll note that both are a lot more complicated than just using gm
as it is intended. ;)
Here's a way to do what you want with an event state machine.
const gm = requre('gm');
const EventEmitter = require('events');
const input_path = './image.jpg'
const output_path = './newimage.jpg';
const worker = new EventEmitter(); // create an event worker
// define the worker states - note: NO error checking! Muy mal!
const event_states={
start:() => worker.emit('resize',gm(input_path)), // creates a new gm image instance
resize:(img) => worker.emit('crop', img.resize(100,100)), // resizes the image
crop:(img) => worker.emit('write', img.crop(2,2,50,50)), // crops it
write:(img) => { // and writes it to the filesystem
img.write(output_path, err => {
if(err) {
worker.emit('error',err);
return;
}
worker.emit('complete');
});
},
error: (err) => console.error(err.toString()), // report error
complete: () => console.log('complete') // alert on completion
};
// add the states to the worker as event handlers
Object.keys(event_states).forEach(k => worker.on(k, event_states[k]));
// and fire it up...
worker.emit('start');
Or if you really, REALLY want to use Promises...
const writer = function (img) {
return new Promise( (resolve, reject) => {
img.write(output_path,err => {
if(err) {
reject(err);
return;
}
resolve(true);
});
});
};
const reader = function (input_path) {
return new Promise( (resolve,reject) => {
let img;
try {
img = gm(input_path);
}
catch (err) {
reject(err);
return;
}
resolve(img);
});
};
reader('./image.jpg')
.then( img => { return img.resize(100,100) }) // the return here is for illustration only
.then( img => img.crop(2,2,50,50)) // since this does exactly the same thing with less typing!
.then( writer )
.then( res => console.log('complete'))
.catch( err => console.error(err.toString()));
Again, lots more typing and complexity all to use the newest "shiny" thing. ;)