使用RxJS和节点调整图片大小

时间:2016-08-31 20:08:39

标签: node.js rxjs graphicsmagick rxjs5

我是RxJS的新手并尝试这个(貌似)简单的任务,但我无法理解。

我想:
1.从文件中读取图像
2.将该图像转换为几个较小的图像
3.将所有图像保存到文件

我已将fs.readFile和fs.writeFile转换为observables。

const readFile$ = Rx.Observable.bindNodeCallback(fs.readFile);
const writeFile$ = Rx.Observable.bindNodeCallback(fs.writeFile);

我制作了一个图片阵列管道。

var pictureSizes = [
  {width: 100, size: 'thumbnail', suffix: '_t'},
  {width: 300, size: 'small', suffix: '_s'},
  {width: 600, size: 'medium', suffix: '_m'},
  {width: 1000, size: 'large', suffix: '_l'}
];

我使用图形魔法

创建了一个resizeImage $函数
function resizeImage$(picture, data) {
  return Rx.Observable.create(observer => {
      gm(data)
        .resize(picture.width)
        .toBuffer('jpg', function(err, buffer) {
          if (err) {
            console.log(err);
            observer.error(err);
          } else {
            observer.next(buffer);
            observer.complete();
          }
        });
  })
}

我认为(希望)以上是可以的。我无法弄清楚如何链接我的操作员。

  readFile$('./largeimage.jpg')
    .mergeMap(data => pictureSizes.map(picture => resizeImage$(picture, data)))
    .flatMap(picture => writeFile$('./testImages/resized.jpg', picture))
    .subscribe(
    (x) => console.log('Next', x),
    (e) => console.log('Error', e),
    (c) => console.log('Complete',c )
  )

以上将此数据损坏为jpeg文件。 (并重写该文件,因为我无法弄清楚如何将pictureSizes.suffix输入到输出的文件名中。

一切都有帮助!谢谢。

更新

我得到了它的工作,但我知道这个奇怪的多重订阅是一个可怕的反模式。主要订阅在调整图像大小之前完成。我觉得这是一个热/冷的问题,但我不知道如何解决它。这是我现在正在使用的代码..

const pictureSizes = [
  {width: 100, size: 'thumbnail', suffix: '_t'},
  {width: 300, size: 'small', suffix: '_s'},
  {width: 600, size: 'medium', suffix: '_m'},
  {width: 1000, size: 'large', suffix: '_l'}
];

const image = 'truck.jpg';

function resizeImage$(binary, pictureSize) {
  return new Rx.Observable(observer => {
      gm(binary)
        .resize(pictureSize.width)
        .toBuffer('jpg', function(err, buffer) {
          console.log('BUFFER');
          if (err) {
            console.log(err);
            observer.error(err);
          } else {
            observer.next({binary: buffer, pictureSize: pictureSize});
            observer.complete('done');
          }
        });
  }).subscribe(
    (resizedImage) => {
      console.log(resizedImage);
      const binary = resizedImage.binary;
      const pictureSize = resizedImage.pictureSize;
      const fileName = image.split('.')[0];
      const fileExtension = image.split('.')[1];
      fs.writeFile(`./testImages/${fileName}${pictureSize.suffix}.${fileExtension}`, binary);
    })
}
  var readFile$ = new  Rx.Observable.bindNodeCallback(fs.readFile);
  readFile$(`./${image}`)
  .zip(Rx.Observable.of(pictureSizes), (binary, sizes) =>
        Rx.Observable.of({ binary: binary, sizes: sizes }))
  .mergeMap(x => x.value.sizes.map(pictureSize => 
       resizeImage$(x.value.binary, pictureSize)))
  .subscribe()

2 个答案:

答案 0 :(得分:0)

如果有人有兴趣,我有答案。如果有人想进一步重构,请这样做。

剩下的问题...... 1.写文件是否应作为可观察的文件来捕获错误? 我不确定为什么需要mergeAll。

var pictureSizes = [
  {width: 100, size: 'thumbnail', suffix: '_t'},
  {width: 300, size: 'small', suffix: '_s'},
  {width: 600, size: 'medium', suffix: '_m'},
  {width: 1000, size: 'large', suffix: '_l'}
];

function scaleImage$(binary, pictureSize) {
  return new Rx.Observable(observer => {
      gm(binary)
        .resize(pictureSize.width)
        .toBuffer('jpg', function(err, buffer) {
          if (err) {
            observer.error(err);
          } else {
            observer.next({ binary: buffer, pictureSize: pictureSize });
            observer.complete();
          }
        });
  })
}

function writeFile(binary, pictureSize, image) {
  const fileName = image.split('.')[0];
  const fileExtension = image.split('.')[1];
  fs.writeFile(`./resized/${fileName}${pictureSize.suffix}.${fileExtension}`, binary);
}

function resizeImage(imagePath) {
  var readFile$ = new  Rx.Observable.bindNodeCallback(fs.readFile);
  readFile$(imagePath)
    .combineLatest(Rx.Observable.of(pictureSizes),(binary,y) => y.map(pictureSize => Object.assign({}, {binary, pictureSize} )))
    .mergeMap(arr => arr.map(obj => scaleImage$(obj.binary, obj.pictureSize)))
    .mergeAll()
    .subscribe((obj) => writeFile(obj.binary, obj.pictureSize, image))

}

如果您想要同步行为(缩放图像1 - >写图像1 - >缩放图像2 ...),请使用concatMap和concatAll。

答案 1 :(得分:0)

根据您的示例,我认为您可以进一步简化您的答案:

var pictureSizes = [
  {width: 100, size: 'thumbnail', suffix: '_t'},
  {width: 300, size: 'small', suffix: '_s'},
  {width: 600, size: 'medium', suffix: '_m'},
  {width: 1000, size: 'large', suffix: '_l'}
];

const scaler$ = Rx.Observable.bindNodeCallback((binary, size, callback) => {
  gm(binary)
   .resize(size.width)
   .toBuffer('jpg', callback);
});

const readFile$ = Rx.Observable.bindNodeCallback(fs.readFile);
const writeFile$ = Rx.Observable.bindNodeCallback(fs.writeFile);

function scaleImage$(sizes) {
  const scales = Rx.Observable.from(sizes);

  return source => 
    source.flatMap(binary => 
      scales.flatMap(
        size => scaler$(binary, size),
        (pictureSize, binary) => ({pictureSize, binary})
      )
    );
}

function resize(imagePath, sizes) {
  return readFile$(imagePath)
    .let(scaleImage$(sizes))
    .flatMap(result => {
      const {pictureSize, binary} = result;
      const [name, ext] = image.split('.');
      return writeFile$(`./resized/${name}${pictureSize.suffix}.${ext}`, binary);
    });
}

使用:

resize(imagePath, pictureSizes)
  .subscribe();