0

Im trying to create an AWS Lambda Function in C# to crop an image which is stored in the S3 bucket. Once cropped, the new image need to be pushed to another bucket. And this function has to be invoked manually using the AWS Lambda Client. As im a beginner and there is not much tutorials available on the web, please help me on this.

This is how my code looks like

 using System;
 using System.Threading.Tasks;
 using System.IO;

 using Amazon.Lambda.Core;
 using Amazon.Lambda.Serialization;
 using Amazon.S3;
 using Amazon;
 using Amazon.S3.Model;
 using Pomelo.Data.MySql;
 using System.Drawing;
 using Amazon.S3.Transfer;
 //using ImageNet;
 //using ImageResizer;

 // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
 [assembly: LambdaSerializerAttribute(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

 namespace Cropper
 {
     public class Crop
     {
         IAmazonS3 s3Client { get; set; }
         private Image orgImg;
         private MySqlConnection dbConn = new MySqlConnection();
         private MySqlCommand cmd = new MySqlCommand(); 

         public Crop()
         {
             s3Client = new AmazonS3Client();
         }

         /*private static string s3AccessKey = Environment.GetEnvironmentVariable("S3AccessKey");
         private static string s3SecretKey = Environment.GetEnvironmentVariable("S3SecretKey");
         private static string s3Region = Environment.GetEnvironmentVariable("S3Region");
         private AmazonS3Client s3Client = new AmazonS3Client(s3AccessKey,s3SecretKey, RegionEndpoint.GetBySystemName(s3Region));
         */

         // public async Task<string> CropImage(string sourceBucket, string key, string destBucket, string permission)
         //public async void CropImage(string sourceBucket, string key, string destBucket, string permission)
         public async Task<string> CropImage(string input, ILambdaContext context)
         {
             s3Client = new AmazonS3Client();

             string[] arr = input.Split(',');
             string sourceBucket = arr[0].Trim();
             string key = arr[1].Trim();
             string destBucket = arr[2].Trim();
             //string size = arr[3].Trim();
             //string crop = arr[3].Trim();
             string permission = arr[3].Trim();

             string path = Path.Combine("/tmp", key);

             try
             {
                 Console.WriteLine("Checkpoint - 0");

                 TransferUtility fileTransferUtility = new TransferUtility(s3Client);
                 TransferUtilityDownloadRequest downloadRequest = new TransferUtilityDownloadRequest();

                 Console.WriteLine("path - " + path);

                 if (File.Exists(path))
                 {
                     File.Delete(path);
                 }
                 downloadRequest.FilePath = path;
                 downloadRequest.BucketName = sourceBucket;
                 downloadRequest.Key = key;
                 fileTransferUtility.Download(downloadRequest);

                 Console.WriteLine("Checkpoint - file transfer");

                 orgImg = Image.FromFile(path);
                 orgImg.RotateFlip(RotateFlipType.Rotate90FlipNone);
                 //FluentImage.FromFile(path)
                 //   .Resize.Width(200)
                 //   .Save("/tmp/test_file.png", OutputFormat.Png);



                 Console.WriteLine("Checkpoint - Img creation");

                 TransferUtilityUploadRequest uploadRequest = new TransferUtilityUploadRequest();
                 //uploadRequest.FilePath = "/tmp/test_file.png";
                 uploadRequest.FilePath = path;
                 uploadRequest.BucketName = destBucket;
                 uploadRequest.Key = key;
                 if (permission.ToUpper() == "PUBLIC")
                 {
                     uploadRequest.CannedACL = S3CannedACL.PublicRead;
                 }
                 else if (permission.ToUpper() == "PRIVATE")
                 {
                     uploadRequest.CannedACL = S3CannedACL.Private;
                 }
                 else if (permission.ToUpper() == "NONE")
                 {
                     uploadRequest.CannedACL = S3CannedACL.NoACL;
                 }
                 fileTransferUtility.Upload(uploadRequest);

                 Console.WriteLine("Checkpoint - Done");

                 return context.AwsRequestId.ToString();
             }
             catch (Exception ex)
             {
                 Console.WriteLine("ex message - " + ex.Message);
                 Console.WriteLine("ex stack - " + ex.StackTrace);
                 return ex.Message;
                 //context.Logger.LogLine(ex.Message);
             }
             finally
             {
                 if (File.Exists(path))
                 {
                     File.Delete(path);
                 }
                 if (orgImg != null)
                 {
                     orgImg.Dispose();
                 }
             }
         }

         //private byte[] ToArrayBytes(Stream input)
         //{
         //    byte[] buffer = new byte[16 * 1024];
         //    using (MemoryStream ms = new MemoryStream())
         //    {
         //        int read;
         //        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
         //        {
         //            ms.Write(buffer, 0, read);
         //        }
         //        return ms.ToArray();
         //    }
         //}

         //private byte[] ImageToByte(Image img)
         //{
         //    ImageConverter converter = new ImageConverter();
         //    return (byte[])converter.ConvertTo(img, typeof(byte[]));
         //}
     }
 }

This is the project.json

 {
   "version": "1.0.0-*",
   "buildOptions": {
   },

   "dependencies": {
     "Microsoft.NETCore.App": {
       "type": "platform",
       "version": "1.0.0"
     },
     "Amazon.Lambda.Core": "1.0.0*",
     "Amazon.Lambda.Serialization.Json": "1.0.1",
     "Amazon.Lambda.Tools": {
       "type": "build",
       "version": "1.0.4-preview1"
     },
     "AWSSDK.S3": "3.3.4.1",
     "Pomelo.Data.MySql": "1.0.0",
     "Microsoft.NETCore.Portable.Compatibility": "1.0.1",
     //"System.Drawing-dotnet-core": "1.0.2",
     //"Shaman.System.Drawing": "1.0.1.1",
     //"ZKWeb.System.Drawing": "2.0.1"
     //"System.Drawing.Primitives": "4.3.0"
     //"ImageProcessor-strong-name": "2.3.0",
     //"Microsoft.NETCore.Runtime.CoreCLR": "1.1.0",
     //"Sitecore.ImageProcessor": "1.1.0",
     //"DynamicImage": "3.1.1",
     //"ImageLibrary": "3.0.0",
     //"FluentImage": "1.1.0",
     "CoreCompat.System.Drawing": "1.0.0-beta006",
     "ImageResizer.Mvc": "4.0.5"
     //"NETStandard.Library": "1.6.1"
     //"omu.drawing": "1.0.0"
   },
     "tools": {
       "Amazon.Lambda.Tools": "1.0.4-preview1"
     },
     "frameworks": {
       "netcoreapp1.0": {
         "imports": [
           "dnxcore50",
           "dotnet5.6",
           "net46"
         ]
       }
     }
 }

And, this is the stack trace

 2017-11-03 23:11:43: START RequestId: e3975f78-d1db-11e6-9923-974cd0941dbb Version: $LATEST
 2017-11-03 23:11:44: Checkpoint - 0
 2017-11-03 23:11:44: path - /tmp/r3_small.jpg
 2017-11-03 23:11:47: Checkpoint - file transfer
 2017-11-03 23:11:47: ex message - The type initializer for 'System.Drawing.GDIPlus' threw an exception.
 2017-11-03 23:11:48: ex stack -    at System.Drawing.GDIPlus.GdipLoadImageFromFile(String filename, IntPtr& image)
 2017-11-03 23:11:48:    at System.Drawing.Image.FromFile(String filename, Boolean useEmbeddedColorManagement)
 2017-11-03 23:11:48:    at System.Drawing.Image.FromFile(String filename)
 2017-11-03 23:11:48:    at Cropper.Crop.<CropImage>d__8.MoveNext()
 2017-11-03 23:11:48: END RequestId: e3975f78-d1db-11e6-9923-974cd0941dbb
 2017-11-03 23:11:48: REPORT RequestId: e3975f78-d1db-11e6-9923-974cd0941dbb    Duration: 4970.70 ms    Billed Duration: 5000 ms    Memory Size: 256 MB Max Memory Used: 63 MB  

Code used to crop and rotate the image

 public void cropLocal( string url, int x, int y, int w, int h, int a)
 {
    try
    {
        if (x != 0 && y != 0 && w != 0 && h != 0)
        {
            if (File.Exists(url))
            {
                orgImg = Image.FromFile(url);
                switch (a)
                {
                    case 90:
                    case -270:
                        orgImg.RotateFlip(RotateFlipType.Rotate90FlipNone);
                        break;
                    case 180:
                    case -180:
                        orgImg.RotateFlip(RotateFlipType.Rotate180FlipNone);
                        break;
                    case 270:
                    case -90:
                        orgImg.RotateFlip(RotateFlipType.Rotate270FlipNone);
                        break;
                }

                if (x < 0) { x = 0; }
                if (y < 0) { y = 0; }
                if (w > orgImg.Width) { w = orgImg.Width; }
                if (h > orgImg.Height) { h = orgImg.Height; }

                bitmap = new Bitmap(orgImg);
                orgImg.Dispose();
                orgImg = null;

                rect = new Rectangle(x, y, w, h);
                cropped = bitmap.Clone(rect, bitmap.PixelFormat);
                bitmap.Dispose();
                bitmap = null;

                //delete original file
                File.Delete(url);
                //Save new file
                cropped.Save(url);
                cropped.Dispose();
                cropped = null;
            }
        }
    }
    catch(Exception ex)
    {
        string excep = ex.Message;
    }
    finally
    {
        if (orgImg != null)
        {
            orgImg.Dispose();
            orgImg = null;
        }
        if (bitmap != null)
        {
            bitmap.Dispose();
            bitmap = null;
        }
        if (cropped != null)
        {
            cropped.Dispose();
            cropped = null;
        }
    }

 }
Suraj KV
  • 76
  • 9
  • If you post the code you have from the experimentation you've done people will jump in and help improve it. Generally this community isn't as helpful if they don't see any attempt to follow the basic tutorials that are out there. – Dave Maple Jan 02 '17 at 21:43
  • 1
    Thanks Dave. Will do it from now on. Appreciate your help and advice.. – Suraj KV Jan 03 '17 at 17:54

2 Answers2

1

Found that its hard to do it in C# at this point of time and i have to learn java instead. See below the Java code to download image from s3, resize (without losing aspect ratio), crop, rotate and upload to back to s3 (to a different bucket). It works perfectly.

 package imageprocessor;

 import java.awt.Graphics2D;
 import java.awt.Rectangle;
 import java.awt.geom.AffineTransform;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.util.UUID;

 import javax.imageio.ImageIO;
 import javax.swing.UIManager;

 import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
 import com.amazonaws.services.s3.AmazonS3Client;
 import com.amazonaws.services.s3.model.CannedAccessControlList;
 import com.amazonaws.services.s3.model.GetObjectRequest;
 import com.amazonaws.services.s3.model.PutObjectRequest;


 public class LambdaImageProcessor implements RequestHandler<String, String> {

    private static final String filePath = "/tmp/";
    private static File localFile, outputfileCropped, outputfileRotated, outputfileSized;
     private static final float xsize = 500;   //max width for resize
     private static final float ysize = 500;   //max height for resize

     @Override
     public String handleRequest(String input, Context context) {

        AmazonS3Client s3Client = new AmazonS3Client(
                 new DefaultAWSCredentialsProviderChain());

         context.getLogger().log("Input: " + input + "\n");

         try
         {
            String[] arr = input.split(",");
            String sourceBucket = arr[0];
            String key = arr[1];
             String destBucket = arr[2];
             String permission = arr[3];

            //download
             context.getLogger().log("AWS S3 downloading image\n");
            GetObjectRequest downloadReq = new GetObjectRequest(sourceBucket, key);

            String uniqueID = UUID.randomUUID().toString().replaceAll("-", "");
            localFile = new File(filePath + uniqueID + "_" + key);
            outputfileCropped = new File(filePath + uniqueID + "_" + "c_" + key);
            outputfileRotated = new File(filePath + uniqueID + "_" + "r_" + key);
            outputfileSized = new File(filePath + uniqueID + "_" + "s_" + key);

            if(localFile.exists())
            {
                localFile.delete();
            }
            if(outputfileCropped.exists())
            {
                outputfileCropped.delete();
            }
            if(outputfileRotated.exists())
            {
                outputfileRotated.delete();
            }

            context.getLogger().log("File name - " + localFile.toString() + "\n");
            s3Client.getObject(downloadReq, localFile);      
            context.getLogger().log("AWS S3 download - Done\n");

            //upload
            if(localFile.exists())
            {
                //context.getLogger().log("AWS S3 cropping image\n");

                BufferedImage bufferedImage = ImageIO.read(localFile);  

                BufferedImage resizeBuffered = resizeImage(bufferedImage, xsize, ysize);
                //ImageIO.write(resizeBuffered, "jpg", outputfileSized);    
                context.getLogger().log("AWS S3 Resize - Done\n");

                Rectangle rect = new Rectangle(100, 100, 300, 150);
                BufferedImage cropBuffered = cropImage(resizeBuffered, rect);
                context.getLogger().log("AWS S3 Crop - Done\n");

                ImageIO.write(rotateImage(cropBuffered, 90), "jpg", outputfileRotated);
                context.getLogger().log("AWS S3 Rotate - Done\n");

                context.getLogger().log("AWS S3 uploading image\n");
                PutObjectRequest uploadReq = new PutObjectRequest(destBucket, key, outputfileRotated);        

                if(permission.toUpperCase().equals("PUBLIC"))
                {
                    uploadReq.setCannedAcl(CannedAccessControlList.PublicRead);
                }
                else if(permission.toUpperCase().equals("PRIVATE"))
                {
                    uploadReq.setCannedAcl(CannedAccessControlList.Private);
                }
                s3Client.putObject(uploadReq);
                context.getLogger().log("AWS S3 upload - Done\n");
            }
            else
            {
                context.getLogger().log("Downloaded file not found\n");
            }
            return context.getAwsRequestId();
         }
         catch (Exception ex)
         {
            context.getLogger().log("Exception - " + ex.getMessage().toString() + " " + ex.getStackTrace().toString() + "\n");
            return "Exception - " + ex.getMessage().toString() + " " + ex.getStackTrace().toString();
         }
         finally 
         {          
            if(localFile.exists())
            {
                localFile.delete();
                context.getLogger().log("Temp Local File Deleted\n");
            }  
            if(outputfileCropped.exists())
            {
                outputfileCropped.delete();
                context.getLogger().log("Temp Cropped File Deleted\n");
            }
            if(outputfileRotated.exists())
            {
                outputfileRotated.delete();
                context.getLogger().log("Temp Rotated File Deleted\n");
            }
            if(outputfileSized.exists())
            {
                outputfileSized.delete();
                context.getLogger().log("Temp ReSized File Deleted\n");
            }
        }
     }

     private static BufferedImage cropImage(BufferedImage src, Rectangle rect) {
       BufferedImage dest = src.getSubimage(rect.x, rect.y, rect.width, rect.height);
       return dest; 
    }

     private static BufferedImage rotateImage(BufferedImage bufferedImage, int angle) { 

        double theta = Math.toRadians(angle);
        double cos = Math.abs(Math.cos(theta));
         double sin = Math.abs(Math.sin(theta));
         double width  = bufferedImage.getWidth();
         double height = bufferedImage.getHeight();
         int w = (int)(width * cos + height * sin);
         int h = (int)(width * sin + height * cos);

         BufferedImage out = new BufferedImage(w, h, bufferedImage.getType());
         Graphics2D g2 = out.createGraphics();
         g2.setPaint(UIManager.getColor("Panel.background"));
         g2.fillRect(0,0,w,h);
         double x = w/2;
         double y = h/2;
         AffineTransform at = AffineTransform.getRotateInstance(theta, x, y);
         x = (w - width)/2;
         y = (h - height)/2;
         at.translate(x, y);
         g2.drawRenderedImage(bufferedImage, at);
         g2.dispose();
         return out; 
    }

     private static BufferedImage resizeImage(BufferedImage bufferedImage, float sizeX, float sizeY)
    {
        float ratioX = sizeX / bufferedImage.getWidth();
        float ratioY = sizeY / bufferedImage.getHeight();
        float ratio = Math.min(ratioX, ratioY);

        float newX = bufferedImage.getWidth() * ratio;
        float newY = bufferedImage.getHeight() * ratio;

        BufferedImage resizedImage = new BufferedImage((int)newX, (int)newY, bufferedImage.getType());
         Graphics2D g = resizedImage.createGraphics();
         g.drawImage(bufferedImage, 0, 0, (int)newX, (int)newY, null);
         g.dispose();

         return resizedImage;

    }

 }
Suraj KV
  • 76
  • 9
0

You'll need to learn how to read an S3 object using the .NET SDK.

You'll need to learn how to upload an image to S3 using the .NET SDK.

You can practice those two operations with no AWS Lambda integration in a test or main method to get your feet wet.

Next you'll want to know how to crop an image in C#. Again you can test this without needing AWS Lambda.

Finally, you should read the AWS Lambda C# documentation and try some simple examples of creating a function. Get a very basic example fully working and then expand upon it adding in the code you've developed above.

Community
  • 1
  • 1
Dave Maple
  • 8,102
  • 4
  • 45
  • 64
  • Hi, Thanks for the update. I have done the first two (S3 and image cropping without lambda). Now im successfully able to download and upload images from one bucket to another using the Lambda function. But im not able to integrate the image processing code into it as the .net core app not supporting the System.Drawing namespace. Any alternative to overcome this? – Suraj KV Jan 03 '17 at 17:15
  • When i use third party libraries from nuget im getting error "The type initializer for 'System.Drawing.GDIPlus' threw an exception." – Suraj KV Jan 03 '17 at 17:22
  • @SurajKV: You get that locally or it works locally and you get that when running it in AWS Lambda? – Dave Maple Jan 03 '17 at 17:25
  • Without lambda yes. With lambda when i am able to build it successfully. Error happening when i invoke it. – Suraj KV Jan 03 '17 at 17:33
  • Looks like the Lambda runtime currently supports .NET Core 1.0.1 (C#) which apparently doesn't have that library -- try adding it as a dependency. – Dave Maple Jan 03 '17 at 17:47
  • Tried the below all as dependencies. But no luck //"System.Drawing-dotnet-core": "1.0.2", //"Shaman.System.Drawing": "1.0.1.1", //"ZKWeb.System.Drawing": "2.0.1" //"System.Drawing.Primitives": "4.3.0" //"ImageProcessor-strong-name": "2.3.0", //"Microsoft.NETCore.Runtime.CoreCLR": "1.1.0", //"Sitecore.ImageProcessor": "1.1.0", //"DynamicImage": "3.1.1", //"ImageLibrary": "3.0.0", //"FluentImage": "1.1.0", "CoreCompat.System.Drawing": "1.0.0-beta006", "ImageResizer.Mvc": "4.0.5" //"NETStandard.Library": "1.6.1" //"omu.drawing": "1.0.0" – Suraj KV Jan 03 '17 at 18:01
  • either the error is "The type initializer for 'System.Drawing.GDIPlus' threw an exception." OR "CS0012 The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'" – Suraj KV Jan 03 '17 at 18:07
  • I'm not seeing the code where you're cropping the image. Are you using `Graphics.FromImage` or another method? Can you post a new section in your question with just the code you're using to crop? – Dave Maple Jan 03 '17 at 18:23
  • Have updated the question with the crop image code The Lambda function fails while reading the input file at "orgImg = Image.FromFile(path);". – Suraj KV Jan 03 '17 at 18:30