1
votes

I have set up my architecture as such:

GET request for /products/:id/100x100/image.jpeg is being sent to CloudFront.

CloudFront will send the 100x100 dimension image.jpeg back to the client if it is cached, else it will call the Origin, which will then call the Lambda function to resize the image. The resized image from the Lambda function will then be put in the S3 Bucket and sent back as the response.

I have removed the ViewerRequest Lambda function. Architecture of my set up This is my NodeJS code:

const AWS = require('aws-sdk');
const S3 = new AWS.S3({
    signatureVersion: 'v4',
});
const Sharp = require('sharp');
const BUCKET = 'xBucket';

exports.handler = (event, context, callback) => {
    let response = event.Records[0].cf.response;

    //check if image is not present
    if (response.status == 404) {
        let request = event.Records[0].cf.request;
        let path = request.uri;
        let key = path.substring(1);
        console.log('key path:', key);

        // parse the prefix, width, height and image name
        // Ex: key=images/200x200/webp/image.jpg
        let prefix, originalKey, match, width, height, requiredFormat, imageName, productId;

        // key=products/12/100x100/image.jpg
        try {
          match = key.match(/(.*)\/(\d+)\/(\d+)x(\d+)\/(.*)/);
          prefix = match[1]; //products
          productId = match[2];
          width = parseInt(match[3], 10); //100
          height = parseInt(match[4], 10);//100

          console.log(`match: ${match} | prefix: ${prefix} | productId: ${productId} | width: ${width} | height: ${height}`);

          // correction for jpg required for 'Sharp'
          requiredFormat = match[5].split('.')[1] === "jpg" ? "jpeg" : match[4].split('.')[1];
          console.log('requiredFormat: ', requiredFormat);
          imageName = match[5];

          originalKey = `${prefix}/${productId}/${imageName}`; // products/12/fjords.jpg
          console.log('Original Key:', originalKey);

          // get the source image file
          S3.getObject({ Bucket: BUCKET, Key: originalKey }).promise()
            .then(data => {
              console.log('data:', data);
              return Sharp(data.Body)
                .resize(width, height)
                .toFormat(requiredFormat)
                .toBuffer()
              })
            .then(buffer => {
              console.log('image resized');
                // save the resized object to S3 bucket with appropriate object key.
                S3.putObject({
                    Body: buffer,
                    Bucket: BUCKET,
                    ContentType: 'image/' + requiredFormat,
                    CacheControl: 'max-age=31536000',
                    Key: key,
                    StorageClass: 'STANDARD'
                }).promise()
                // even if there is exception in saving the object we send back the generated
                // image back to viewer below
                .catch((err) => { console.log(`Exception while writing resized image to bucket: ${err}`)});

                console.log('resized imaged updated');

                // generate a binary response with resized image
                response.status = 200;
                response.body = buffer.toString('base64');
                response.bodyEncoding = 'base64';
                response.headers['content-type'] = [{ key: 'Content-Type', value: 'image/' + requiredFormat }];

                console.log('response event:', event);

                console.log('FINAL RESPONSE: ', response);

                callback(null, response);
              })
            .catch(err => { console.log("Exception while reading source image :%j",err) })
        } catch (e) {
          console.log(e);
        }
    } // end of if block checking response statusCode
    else {
        // allow the response to pass through
        callback(null, response);
    }
};

Based on AWS docs, the 502 Error could be due to Lambda Validation Error where the response returned from OriginResponseLamda does not conform to the structure of Lambda@Edge event structure. https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/http-502-lambda-validation-error.html

The error is: 502 ERROR The request could not be satisfied. The Lambda function returned invalid json: The json output must be an object type.

Not sure why the Lambda function logs is not found in CloudWatch also..

2
Which case are you testing first when the image is cached or not cached?Ali
Remember that the response-status should be a string - '200'. TBH I still can't get past that erro message either.exception

2 Answers

1
votes

If you still didn't manage to solve the issue, then you may check:

  • the maximum size of the response body (1MB).
    If larger, it's better to return a 302 to the rendered image.

  • the timeout settings of the function (i.e. if it takes more than 30 seconds)

Source:
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-lambda-at-edge

0
votes

In my case, the problem was in a misconfiguration at the origin. My CloudFront distribution was configured to use subdirectories:

CloudFront Origins: /prod, /sand, /dev

Which was linked to different folders at the S3 bucket:

S3 Buckets: /prod, /sand, /dev

As soon as I removed the subdirectories and configured everything to work at the default path/directory (/), it worked fine.