发出使用NodeJS和GraphicsMagick

时间:2016-05-30 15:41:45

标签: javascript node.js express graphicsmagick

我即将废弃这个当前的NodeJS项目,因为我已经做了一些简单的事情,比如创建多分辨率的图像副本并为其中一个加水印。我花了几天时间与Node争夺这项工作,这是荒谬的,因为Ruby中的相同代码只有几行而且需要花费几分钟才能完成。然而,公司想要它在Node中,所以我们走了。

使用NodeJS,Express,Multer和GraphicsMagick我正在尝试拍摄用户上传的高清图像并创建多个较小的副本,最后的缩略图被加水印。这本身就相当容易,但是当我开始进行数百次快速上传的负载测试时,我发现尽管上传了原始图像,但我还是丢失了大约20%的复制图像。我对正在发生的事情的猜测是,当写入磁盘时,复制功能被中断,因为下一次上传过程中,JavaScript正在重写文件名或其他内容。

正如您在下面的代码中所看到的,每次调用post处理程序时我都会创建一个新对象,我假设它会封装方法变量并阻止任何被覆盖的内容。老实说,我不知道JavaScript在这里做了什么。

我的测试方法是一个自定义Bash脚本,它通过Curl发布文件。我已经在Ubuntu Xenial和Debian上测试了这个,结果相同。下面的脚本只上传了20张图片,但问题仍然存在,它预计会返回40张图片,但会返回36左右:

#!/bin/bash

#simulates multiple image posts with different parameters
for run in {1..2}
do
    FNAME=("imageOne" "imageTwo" "imageThree" "imageFour" "imageFive" "imageSix" "imageSeven" "imageSeven" "imageEight" "imageNine" "imageTen")
    USER=("userOne" "userTwo" "userThree" "userFour" "userFive" "userSix" "userSeven" "userSeven" "userEight" "userNine" "userTen")
    for item in {0..9}
    do
        URL="http://localhost:3000/api/images/${USER[$item]}/${FNAME[$item]}"
        curl -i -F "image=@sample.png;type=image/png" $URL

# works when sleep called
#        sleep 1

    done
done 

正如在评论中所说,当在每次上传之前插入间隔时,代码完美地工作,这意味着问题是由异步执行引起的。也许我的回调不会等到确认正在写的文件?

以下是主要方法。 Upload方法使用Multer上传用户图像。 FindSize获取水印和用户图像并查找尺寸以供以后处理,Watermark创建原始文件的副本。我已经为了这个演示删除了所有其他功能,例如创建水印并调整大小到不同的分辨率,因为它仍然会遇到同样的问题。

"use strict";
var multer = require('multer'),
    fs = require('fs'),
    gm = require('gm');


function ProcessFile(req,res){

    var fname,
        fmime;

    var storage = multer.diskStorage({
        destination: function (req, file, cb) {

            cb(null, './uploads/');
        },
        filename: function (req, file, cb) {
            fmime = '.' + file.mimetype.substr(6,file.mimetype.length);
            fname = req.params.user + '-' + Date.now() + '-' + req.params.filename;
            cb(null, fname + fmime);
        }
    });

    // multer option that controls which files are uploaded
    var fileFilter = function(req,file,cb)
    {
        // remove non-raster-image filetypes
        var mimetypes = ['gif','jpeg', 'png', 'bmp'];
        for (var mime in mimetypes)
        {
            if (file.mimetype == 'image/' + mimetypes[mime])
            {
                // accept file
                cb(null,true);
                return;
            }
        }
        // reject file
        cb(null,false);
    };

    var upload = multer({storage:storage, fileFilter:fileFilter, limits:{fileSize:1000000}}).single('image');


    var findSize = function(call){
        var yPos = 0,
            xPos = 0,
            self = this,
            width = 0,
            height = 0;

        // get width of watermark image
        function getWidth(c) {
            gm('watermark.png')
                .size(function (err, size) {
                    width = size.width;
                    height = size.height;
                    c(); // getMidPoint callback
                });
        }

        // finds horizontal mid point of image and calls main function
        function getMidPoint(c) {
            gm('./uploads/' + fname + fmime)
                .size(function (err, size) {
                    if (err) {
                        console.log(err)
                    }
                    else {
                        yPos = size.height / 2;
                        xPos = size.width / 2;
                        c();
                    }
                });
        }

        getWidth(function c(){
            getMidPoint(function c(){
                call(yPos, xPos, width, height);
            });
        });

    };

    var watermark = function(yPos, xPos, width, height, call){
        // set watermark width to 60% of image width
        var markwidth = (xPos*2)* 0.6,
            xPlacement = xPos-(markwidth/2),
            yPlacement = yPos-(height/2),
            aspectHeight = (yPos*2)/(xPos*2) * markwidth,
            self = this;

        aspectHeight = Math.round(aspectHeight);
        // does nothing but create a copy for demo purposes
        gm('./uploads/'+fname + fmime)
        // write original image
            .write('./uploads/'+fname+'-'+markwidth+'x'+aspectHeight + fmime, function (err) {
                if (err){console.log(err)}
                else
                {
                    call();
                }
            });
    };


    this.run = function(req,res){
        upload(req,res, function(err){
            if(!err) {
                findSize(function call(yPos, xPos, width, height) {
                    watermark(yPos, xPos, width, height, function call() {
                        console.log('complete');
                    });
                });
            }
        });
    }
}

var p = new ProcessFile();

module.exports = p;

以下是路线的代码。图像处理方法作为变量' imageHandler'。

导入
app.post('/api/images/:user/:filename', function(req,res){
    imageHandler.run(req,res);
    res.end();
});

我仍然试图理解服务器上异步,单线程,事件驱动语言的好处,除非在特殊情况下,这对我来说显然是荒谬的。

无论如何,如果有人可以提供这方面的帮助,我将非常感激,否则我会废弃这个并回到铁轨上。

0 个答案:

没有答案