Node.js,Express和GM(GraphicsMagick) - 添加水印和一般图像处理效率

时间:2014-06-23 21:43:11

标签: node.js graphicsmagick

我需要从上传的照片中创建4个图片尺寸:大,中,小和超小。它大部分都使用下面的代码工作,但是大中型尺寸需要在左下方放置一个水印,而不是较小的两个尺寸。

对于带水印的图像,我似乎需要复制文件流并为每个文件保存一个单独的实例。速度和效率在这里很重要,所以我想确保我做到最好。

GM节点模块真的缺乏文档。有关更多信息,他们会链接到GraphicsMagick网站,如果您尝试使用GM模块执行此操作,则无法提供帮助。这真的令人沮丧。

基本上,我可以帮助找出如何在两个较大尺寸上进行水印,然后我也想知道下面的代码是否尽可能高效。在本地计算机上创建4种尺寸时似乎有点慢。

var fileName = req.files.photo.name,
    fileBaseName = fileName.substr(0, fileName.lastIndexOf('.')),
    uploadRoot = SiteConfig.root + '/upload/',
    photosRoot = SiteConfig.root + '/photos/',
    publicRoot = SiteConfig.root + '/public/';

require('fs').rename(
    req.files.photo.path,
    uploadRoot + fileName,
    function(error)
    {
        if (error)
        {
            res.send({ error: 'upload error' });
            return;
        }

        var ImageSizes = {
            large: {
                width: 990,
                height: 990
            },
            medium: {
                width: 550,
                height: 550
            },
            small: {
                width: 145,
                height: 145
            },
            xsmall: {
                width: 55,
                height: 55
            }
        };

        var GM = require('gm'),
            fileStream = require('fs').createReadStream(photosRoot + fileName);

        var lgPath = photosRoot + fileBaseName + '_lg.jpg',
            mdPath = photosRoot + fileBaseName + '_md.jpg',
            smPath = photosRoot + fileBaseName + '_sm.jpg',
            xsPath = photosRoot + fileBaseName + '_xs.jpg';

        // I'm guessing the second parameter is to set the format
        GM(fileStream, 'img.jpg')
            .size(
                {
                    bufferStream: true
                },
                function(err, size)
                {
                    console.log(size.width);
                    console.log(size.height);

                    if (size.width > ImageSizes.large.width || size.height > ImageSizes.large.height)
                        this.resize(ImageSizes.large.width, ImageSizes.large.height);

                    // Auto-orient based on EXIF data then remove EXIF data
                    this
                        .autoOrient()
                        .noProfile()
                        .quality(70)
                        .write(
                            lgPath,
                            function (err)
                            {
                                if (!err)
                                {
                                    console.log('write large done');

                                    this
                                        .resize(ImageSizes.medium.width, ImageSizes.medium.height)
                                        // watermark code - i want to continue using the file stream instead of a file path
                                        //.subCommand('composite')
                                        //.gravity('Center')
                                        //.in('-compose', 'Over', watermarkFilePath, baseFilePath)
                                        .quality(70)
                                        .write(
                                            mdPath,
                                            function (err)
                                            {
                                                if (!err)
                                                {
                                                    console.log('write medium done');

                                                    this
                                                        .resize(ImageSizes.small.width, ImageSizes.small.height)
                                                        .crop(ImageSizes.small.width, ImageSizes.small.height)
                                                        .quality(70)
                                                        .write(
                                                            smPath,
                                                            function (err)
                                                            {
                                                                if (!err)
                                                                {
                                                                    console.log('write small done');

                                                                    this
                                                                        .resize(ImageSizes.xsmall.width, ImageSizes.xsmall.height)
                                                                        .quality(70)
                                                                        .write(
                                                                            xsPath,
                                                                            function (err)
                                                                            {
                                                                                if (!err)
                                                                                    console.log('write xsmall done');
                                                                                else
                                                                                    console.log('write xsmall error');
                                                                            }
                                                                        );

                                                                }
                                                                else
                                                                    console.log('write small error');
                                                            }
                                                        );
                                                }
                                                else
                                                    console.log('write medium error');
                                            }
                                        );
                                }
                                else
                                    console.log('write large error');
                            }
                        );


                }
            );
    }
);

1 个答案:

答案 0 :(得分:1)

(注意这不是真正的代码,只是关于真实代码可能看起来像什么的想法)

让我觉得你想做这样的事情:

server.on('incomingImage', function (image, res) {
  async.each(imageOptions, function (imageOptions, done) {
     image
       .pipe(resize(imageOptions))
       .pipe(fs.createWriteStream(baseDir + image.path + imageOptions.path))
       .on('end', done);
  }, function (err) {
    if (err) {
       res.send(500);
    } else {
       res.send(204);
    }
  });
});

也就是说,当图像进入时,为每个图像选项创建一个通过流和文件写入流的大小调整,然后通过所有调整大小的流将图像传输到文件写入流,一旦所有文件都有写完了,回复。

通过并行处理流,您可以最大限度地减少服务器因IO而必须等待的任何时间(这是node.js背后的基本思想)。

您正在将文件写入磁盘,然后依次处理每个映像选项并将其写入磁盘,然后再开始从磁盘读取文件。

这样写入时间x 1 +读取时间x 5 +处理时间x 5 +写入时间x 5

相反,它应该是最大处理时间+最大写入时间,这要短得多。

由于gm模块没有提供良好的流接口,因此存在一些复杂情况。您可能想看看是否有更好的模块。

还有关于流的主题:

https://github.com/substack/stream-handbook http://nodeschool.io/#stream-adventure

和async

http://nodeschool.io/#asyncyou

相关问题