Reputation: 76
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;
}
}
}
Upvotes: 0
Views: 2310
Reputation: 76
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;
}
}
Upvotes: 1
Reputation: 8402
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.
Upvotes: 0