Search code examples
node.jsamazon-s3bufferaws-lambdagm

gm Error in aws lambda: string yields empty buffer


I'm trying to upload a buffer to s3 after doing an auto-orient transformation using amazon lambda. I'm sending the buffer from a Nodejs api like so:

var data = request.payload;
          if (data.file) {
            var Media = request.server.plugins.dogwater.media;

            var name = data.file.hapi.filename;
            var local_path = "./uploads/" + name;
            var file = fs.createWriteStream(local_path);
            console.log('adding media...')
            file.on('error', function(err) {
              console.error(err)
            });

            data.file.pipe(file);

            data.file.on('end', function(err) {
              var pic = {
                gallery_id: request.params.gallery_id,
                type: 1
              };
              console.log("\n\n\ndata.file = ",data.file)
  
              Media.create(pic)
                .then(function(media) {
                  httpRequest({
                      url: 'secret',
                      method: 'POST',
                      json: {
                          media_id: media._id,
                          file_name: data.file.hapi.filename,
                          bucket: 'secret',
                          file: data.file
                      },
                      headers: {
                        'x-api-key': 'someapikey'
                      }

When I console log data.file._data in the api I get something like so:

  _data: <Buffer ff d8 ff e0 00 10 4a 46 49 46 00 01 01 01 00 48 00 48 00 00 ff e2 0b f8 49 43 43 5f 50 52 4f 46 49 4c 45 00 01 01 00 00 0b e8 00 00 00 00 02 00 00 00 ... >,

In the lambda (also nodejs) I have this code:

// dependencies
var async = require('async');
var AWS = require('aws-sdk');
var gm = require('gm').subClass({
    imageMagick: true
});
var util = require('util');
var path = require('path');

// get reference to S3 client
var s3 = new AWS.S3();
exports.handler = function(event, context) {

    // payload data
    var mediaId = event.media_id;
    var filename = event.file_name;
    var bucket = event.bucket;
    var file = event.file._data;

    console.log("file = ",file);

    // Infer the image type.
    var typeMatch = filename.match(/\.([^.]*)$/);
    if (!typeMatch) {
        console.error('unable to infer image type for file ' + filename);
        return;
    }
    var imageType = typeMatch[1].toLowerCase();

    var acceptedTypes = ['jpg','gif','png','eps','jpeg'];
    if (acceptedTypes.indexOf(imageType)===-1) {
        console.log('skipping non-image ');
        return;
    }


    async.waterfall([
        function process(next) {
            // Transform the image buffer in memory.
            gm(file).autoOrient().toBuffer(
                imageType.toUpperCase(), function(err,
                    buffer) {
                    if (err) {
                        //newly added line below:
                        console.log(err);
                        console.log("hit");
                        next(err);
                    } else {
                        console.log("buffer = ",buffer);
                        next(null,buffer);
                    }
                }
            );
        },
        function upload(data, next) {
            // Stream the image to folder.
            s3.putObject({
                Bucket: bucket,
                Key: mediaId + "/" + filename,
                Body: data,
                ContentType: imageType.toUpperCase()
            }, next);
        }
        ], function(err, result) {
            if (err) {
                console.error(err);
            }
            // return data to api here
            context.done();
        }
    )// end async waterfall
}// end exports handler

So. When I try to upload an image this is what file._data prints as before gm command:

 {type:'Buffer',data:[234,234,234,random numbers etc ...]}

But within the gm command this prints as the err:

Error: Stream yields empty buffer


Solution

  • Found out the issue-

    Data passed in to the aws lambda had to be remade into a buffer object to be accepted by gm. Changed one line of code (Inside exports.handler, where I'm defining file)

    // dependencies
    var async = require('async');
    var AWS = require('aws-sdk');
    var gm = require('gm').subClass({
        imageMagick: true
    });
    var util = require('util');
    var path = require('path');
    
    // get reference to S3 client
    var s3 = new AWS.S3();
    exports.handler = function(event, context) {
    
        // payload data
        var mediaId = event.media_id;
        var filename = event.file_name;
        var bucket = event.bucket;
        var file = new Buffer(event.file._data);
    
        console.log("file = ",file);
    
        // Infer the image type.
        var typeMatch = filename.match(/\.([^.]*)$/);
        if (!typeMatch) {
            console.error('unable to infer image type for file ' + filename);
            return;
        }
        var imageType = typeMatch[1].toLowerCase();
    
        var acceptedTypes = ['jpg','gif','png','eps','jpeg'];
        if (acceptedTypes.indexOf(imageType)===-1) {
            console.log('skipping non-image ');
            return;
        }
    
    
        async.waterfall([
            function process(next) {
                // Transform the image buffer in memory.
                gm(file).autoOrient().toBuffer(
                    imageType.toUpperCase(), function(err,
                        buffer) {
                        if (err) {
                            //newly added line below:
                            console.log(err);
                            console.log("hit");
                            next(err);
                        } else {
                            console.log("buffer = ",buffer);
                            next(null,buffer);
                            //next(null, 'done');
                        }
                    }
                );
            },
            function upload(data, next) {
                // Stream the image to folder.
                s3.putObject({
                    Bucket: bucket,
                    Key: mediaId + "/" + filename,
                    Body: data,
                    ContentType: imageType.toUpperCase()
                }, next);
            }
            ], function(err, result) {
                if (err) {
                    console.error(err);
                }
                // return data to api here
                context.done();
            }
        )// end async waterfall
    }// end exports handler