0

I am developing an AWS Lambda function project using C# Dot NET Core. What I am doing now is I am resizing the image when an image file is uploaded to s3. This is my project structure.

enter image description here

I have three projects in the single solution. AwsLambda.Domain project is the Dot NET Framework project and AwsLambda is the Dot NET Core project. The Aws Lambda project has the dependency on the AwsLambda.Domain project. Basically, in the AwsLambda.Domain project, I created a new class called ImageHelper.cs which is responsible for resizing the image using features from the Dot NET Framework.

This is my ImageHelper.cs class under AwsLambda.Doamin project.

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace AwsS3Lambda.Domain
{
   public class ImageHelper
   {
      public string CreateThumbImage(string sourcePath, string destPath, int width, int height)
      {
         Image imgOriginal = Image.FromFile(sourcePath);
         Image imgActual = Scale(imgOriginal, width, height);
         imgActual.Save(destPath);
         imgActual.Dispose();
         return destPath;
      }


      private Image Scale(Image imgPhoto, int width, int height)
      {
         float sourceWidth = imgPhoto.Width;
         float sourceHeight = imgPhoto.Height;
         float destHeight = 0;
         float destWidth = 0;
         int sourceX = 0;
         int sourceY = 0;
         int destX = 0;
         int destY = 0;

         // force resize, might distort image
         if(width != 0 && height != 0)
         {
            destWidth = width;
            destHeight = height;
         }
         // change size proportially depending on width or height
         else if(height != 0)
         {
            destWidth = (float)(height * sourceWidth) / sourceHeight;
            destHeight = height;
         }
         else
         {
            destWidth = width;
            destHeight = (float)(sourceHeight * width / sourceWidth);
         }

         Bitmap bmPhoto = new Bitmap((int)destWidth, (int)destHeight,
                                     PixelFormat.Format32bppPArgb);
         bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);

         Graphics grPhoto = Graphics.FromImage(bmPhoto);
         grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;

         grPhoto.DrawImage(imgPhoto,
             new Rectangle(destX, destY, (int)destWidth, (int)destHeight),
             new Rectangle(sourceX, sourceY, (int)sourceWidth, (int)sourceHeight),
             GraphicsUnit.Pixel);

         grPhoto.Dispose();

         return bmPhoto;
      }
   }
}

This is my lambda function class that is using ImageHelper.cs class to resize the uploaded image.

 public class Function
    {
      public static string IAM_KEY { get { return "xxx"; } }
      public static string IAM_SECRET { get { return "xxx"; } }
      public static string TEMP_IMG_FILE_PATH { get { return @"../../tmp/temp_img.jpeg"; } }
      public static string TEMP_RESIZED_IMG_PATH { get { return @"../../tmp/resized_img.jpeg"; } }

      private IAmazonS3 S3Client { get; set; }
      private ImageHelper imageHelper { get; set; }


      public Function()
      {
         S3Client = new AmazonS3Client();
         imageHelper = new ImageHelper();
      }


      public Function(IAmazonS3 s3Client)
      {
         this.S3Client = s3Client;
         this.imageHelper = new ImageHelper();
      }

      public async Task<string> FunctionHandler(S3Event evnt, ILambdaContext context)
      {
         var s3Event = evnt.Records?[0].S3;
         if(s3Event == null)
         {
            return null;
         }

         try
         {
            if(s3Event.Object.Key.ToLower().Contains("thumb"))
            {
               //Console.WriteLine("The image is already a thumb file");
               return "The file is aready a thumb image file";
            }

            string[] pathSegments = s3Event.Object.Key.Split('/');
            string eventCode = pathSegments[0];
            string userEmail = pathSegments[1];
            string filename = pathSegments[2];
            string extension = Path.GetExtension(filename); //.jpeg with "dot"
            string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filename);

            using(var objectResponse = await this.S3Client.GetObjectAsync(s3Event.Bucket.Name, s3Event.Object.Key))
            {
               using(Stream responseStream = objectResponse.ResponseStream)
               {
                  this.saveFile(responseStream, Function.TEMP_IMG_FILE_PATH);

                  imageHelper.CreateThumbImage(Function.TEMP_IMG_FILE_PATH, Function.TEMP_RESIZED_IMG_PATH, 200, 200);

                  ///This code is throwing error
                  await this.S3Client.PutObjectAsync(new Amazon.S3.Model.PutObjectRequest
                  {
                     BucketName = s3Event.Bucket.Name,
                     Key = fileNameWithoutExtension + ".thumb" + extension,
                     FilePath = Function.TEMP_RESIZED_IMG_PATH
                  });
               }
            }

            return "Thumbnail version of the image has been created";

         }
         catch(Exception e)
         {
            context.Logger.LogLine($"Error getting object {s3Event.Object.Key} from bucket {s3Event.Bucket.Name}. Make sure they exist and your bucket is in the same region as this function.");
            context.Logger.LogLine(e.Message);
            context.Logger.LogLine(e.StackTrace);
            throw;
         }
      }

      private void saveFile(Stream stream, string path)
      {
         using(var fileStream = File.Create(path))
         {
            //stream.Seek(0, SeekOrigin.Begin);
            stream.CopyTo(fileStream);
         }

      }
   }

When I test the function in the console, it gave me this error regarding to the Image resizing features implemented under AwsLambda.Domain project (Dot NET Framework).

{
  "errorType": "AggregateException",
  "errorMessage": "One or more errors occurred. (Could not load type 'System.Drawing.Image' from assembly 'System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.)",
  "stackTrace": [
    "at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)",
    "at lambda_method(Closure , Stream , Stream , LambdaContextInternal )"
  ],
  "cause": {
    "errorType": "TypeLoadException",
    "errorMessage": "Could not load type 'System.Drawing.Image' from assembly 'System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.",
    "stackTrace": [
      "at AwsS3Lambda.Domain.ImageHelper.CreateThumbImage(String sourcePath, String destPath, Int32 width, Int32 height)",
      "at AwsS3Lambda.Function.<FunctionHandler>d__18.MoveNext() in C:\\Users\\Acer\\Documents\\Visual Studio 2017\\Projects\\AwsS3Lambda\\AwsS3Lambda\\Function.cs:line 106"
    ]
  },
  "causes": [
    {
      "errorType": "TypeLoadException",
      "errorMessage": "Could not load type 'System.Drawing.Image' from assembly 'System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.",
      "stackTrace": [
        "at AwsS3Lambda.Domain.ImageHelper.CreateThumbImage(String sourcePath, String destPath, Int32 width, Int32 height)",
        "at AwsS3Lambda.Function.<FunctionHandler>d__18.MoveNext() in C:\\Users\\Acer\\Documents\\Visual Studio 2017\\Projects\\AwsS3Lambda\\AwsS3Lambda\\Function.cs:line 106"
      ]
    }
  ]
}

Why is that throwing that error. The image resizing class code is working when I used it in the ASP.NET MVC project. It just does not work for this AWS Lambda function project. What is missing in my project? What would be the other way around to resize the uploaded image in AWS Lambda function project which is based on Dot NET Core?

Wai Yan Hein
  • 13,651
  • 35
  • 180
  • 372
  • On Amazon you can only use .NET Core. You cannot use the regular .NET Framework. You need a library to [manipulate](https://stackoverflow.com/q/33344200/5045688) images under .NET Core. – Alexander Petrov May 25 '18 at 15:21
  • So. You mean I cannot use System.Drawing? I use SkiaSharp library for .net core as well. It is giving this error, The type initializer for 'SkiaSharp.SKAbstractManagedStream' threw an exception. – Wai Yan Hein May 25 '18 at 15:27
  • 1
    I used the Drawing dll for core version as well. Not working. Cannot use any image library installed nuget. Issue might be because AWS lambda is using linux and need some dependencies. – Wai Yan Hein May 25 '18 at 20:31

1 Answers1

0

My assumption is your c# code are going to run under Linux machine which linux is not supporting System.Drawing lib. you might need to make a bridge to import drawing library to linux then.