来自 pngjs 库的方法在 Node.js 中表现得很奇怪

Method from the pngjs library acts weirdly in Node.js

我按 score 值对图像进行排序,该值由 getImageScore 方法计算得出,它包含在一个承诺中,因为加载像素需要相当长的时间它必须与之合作。我观察到承诺被阻止,永远不会完成。这是最初的整个程序:

const fs = require('fs');
const { resolve } = require('path');
const { reject } = require('q');
const { Console } = require('console');
const gm = require('gm').subClass({imageMagick: true});
const PNG = require("pngjs").PNG;
let pathToFolder = '/home/eugen/Pictures/wallpapers1';
let pathToImage = '';

let promiseImageScore = new Promise((resolve, reject) => {
  getImageScore(resolve, reject);
});

function getImageScore(resolve, reject) {
  console.log('entered this promise....');
  let img = gm(pathToImage);
  // Get the PNG buffer
  img.toBuffer("PNG", (err, buff) => {
    if (err) return reject(err);
    console.log('got buffer...');
    // Get the image size
    img.size((err, size) => {
      if (err) {
        console.log(err);
        return reject(err);
      }
      console.log('got image size...');
      // Parse the PNG buffer
      let str = new PNG();
      str.end(buff);
      // After it's parsed...
      str.on("parsed", buffer => {
        // Get the pixels from the image
        let idx, score = 0, rgb = {r: 0, g: 0, b: 0};

        for (let y = 0; y < size.height; y++)
          for (let x = 0; x < size.width; x++) {
            idx = (size.width * y + x) << 2;
            rgb.r = buffer[idx];
            rgb.g = buffer[idx + 1];
            rgb.b = buffer[idx + 2];
            score += (rgb.r + rgb.g + rgb.b) / 765;
          }
          console.log('one promise finished...');
          return resolve(score / (size.height * size.width));
      });
      str.on("error", e => {
        return reject(e);
      });
    });
  });
}

// see which images are to be found in the specificd directory
fs.readdir(pathToFolder, function (err, files) {
    if (err) return console.log('Unable to scan directory: ' + err);
    console.log('files in directory:\n');
    files.forEach(function (file) {
        pathToImage = pathToFolder + '/' + file;
        //showImageScore();
        promiseImageScore
        .then(imageScore => {
          console.log(file + ' has a score of ' + imageScore);
        })
        .catch(e => {
          throw e;
        })
    });
});

运行 上面的代码会导致这个输出:

entered this promise....
files in directory:

got buffer...

记录 got buffer 消息后,程序会连续 运行 ......我看到,通过修改我吸引图像的方式,我最终会得到 got image size 登录控制台。因此,这里是我修改 getImageScore 方法的方式:

function getImageScore(resolve, reject) {
  console.log('entered this promise....');
  //let img = gm(pathToImage);
  // Get the PNG buffer
  //img.toBuffer("PNG", (err, buff) => {
  gm(pathToImage).toBuffer("PNG", (err, buff) => {
    if (err) return reject(err);
    console.log('got buffer...');
    // Get the image size
    //img.size((err, size) => {
    gm(pathToImage).size((err, size) => {
      if (err) {
        console.log(err);
        return reject(err);
      }
      console.log('got image size...');
      // Parse the PNG buffer
      let str = new PNG();
      console.log('created str...');
      str.end(buff);
      console.log('got str...');
      // After it's parsed...
      str.on("parsed", buffer => {
        // Get the pixels from the image
        let idx, score = 0, rgb = {r: 0, g: 0, b: 0};

        for (let y = 0; y < size.height; y++)
          for (let x = 0; x < size.width; x++) {
            idx = (size.width * y + x) << 2;
            rgb.r = buffer[idx];
            rgb.g = buffer[idx + 1];
            rgb.b = buffer[idx + 2];
            score += (rgb.r + rgb.g + rgb.b) / 765;
          }
          console.log('one promised finished...');
          return resolve(score / (size.height * size.width));
      });
      str.on("error", e => {
        return reject(e);
      });
    });
  });
}

进行这些更改后,我在控制台中得到以下输出:

entered this promise....
files in directory:

got buffer...
got image size...
created str...
events.js:174
      throw er; // Unhandled 'error' event
      ^

Error: Invalid file signature
    at module.exports.Parser._parseSignature (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/parser.js:53:18)
    at module.exports.ChunkStream._processRead (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:174:13)
    at module.exports.ChunkStream._process (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:193:14)
    at module.exports.ChunkStream.write (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:61:8)
    at module.exports.ChunkStream.end (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:74:10)
    at exports.PNG.PNG.end (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/png.js:98:16)
    at gm.size (/home/eugen/Documents/scripts/sort_pictures_by_brightness/index.js:34:11)
    at gm.emit (events.js:198:13)
    at gm.<anonymous> (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/gm/lib/getters.js:82:14)
    at cb (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/gm/lib/command.js:322:16)
Emitted 'error' event at:
    at module.exports.emit (events.js:198:13)
    at module.exports.ChunkStream._process (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:207:10)
    at module.exports.ChunkStream.write (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:61:8)
    [... lines matching original stack trace ...]
    at cb (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/gm/lib/command.js:322:16)
    at ChildProcess.onExit (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/gm/lib/command.js:305:9)

从输出来看,我们可以说 str.end(buff); 行有问题,因为程序从不输出 got str 日志。在我对 getImageScore 方法进行更改之前,这个问题似乎并不存在。首先,我真的不明白为什么在本地对象中加载图像会导致代码出现意外行为。其次,修改图像的加载方式不应改变 pngjs 库中的 end 方法。有人可以解释这里实际发生的情况以及如何解决这个问题吗?

幸运的是,来自 Facebook 的人 (Iulian Popescu) 告诉我 files.forEach 没有等待 promise 完成,因此他们被阻止了。我设法通过添加更多承诺来解决这种情况。例如,其中一个承诺读取所有数据。在读取所有数据并将其保存在一些全局变量中后,其他承诺将以正确的顺序执行。如果您想真正看到我正在谈论的解决方案的实施,您可以在我的 GitHub 上查看:https://github.com/tomaAlex/darkImageClassifier/blob/master/index.js