将节点 gm 与承诺和缓冲区一起使用

Use node gm with promises and buffers

我一直在尝试像这样将 gm 与 Bluebird 一起使用:

var gm = require('gm');
var bluebird = require('bluebird');
gm = bluebird.promisifyAll(gm);

但是,当我尝试这样的事情时:

gm(req.file.buffer)
     .crop(tWidth, tHeight, tWidth/2, tHeight/2)
     .gravity('Center')
     .toBuffer()
  .then(...)

我收到此错误:

gm().toBuffer() expects a callback.

如果我只承诺缓冲方法:

bluebird.promisify(gm(req.file.buffer)
        .crop(tWidth, tHeight, tWidth/2, tHeight/2)
        .gravity('Center')
        .toBuffer)()
        .then(buff => {...})

我收到这个错误:

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)

如果我不使用 promises,它工作得很好。

这并不是您应该如何使用 gm 模块,因为它旨在提供工厂 gm() 和许多可级联的突变函数,例如 .resize()

Bluebird 的 .promisifyAll() 仅适用于接受回调的函数,gm 的大多数函数不接受回调。

如果您希望将 gm 与 promise 一起使用,您需要自己 "promisify" 将调用包装为

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

之后,您可以

mutateAndSave()
  .then(...)
  .catch(...);

更新

这里有两种方法可以满足您的需求,但是...

您会注意到,两者都比仅按预期使用 gm 复杂得多。 ;)

这是一种使用事件状态机执行所需操作的方法。

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'); 

或者,如果您真的非常想使用 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()));

同样,更多的输入和复杂性都是为了使用最新的 "shiny" 东西。 ;)