Skip to content

Commit 5587b8e

Browse files
committed
renamed image resizing patterns to be more clear
1 parent 63a7ca7 commit 5587b8e

File tree

17 files changed

+1041
-0
lines changed

17 files changed

+1041
-0
lines changed

s3-lambda-resizing-dotnet/.gitignore

Lines changed: 402 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.6.33815.320
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageResize", "ImageResize\ImageResize.csproj", "{FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {3B666636-4024-4B31-80A6-CB25C208D7F9}
24+
EndGlobalSection
25+
EndGlobal
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
using System.IO;
2+
using SixLabors.ImageSharp;
3+
using SixLabors.ImageSharp.Processing;
4+
using SixLabors.ImageSharp.PixelFormats;
5+
using Amazon.S3.Model;
6+
using SixLabors.ImageSharp.Formats.Jpeg;
7+
using Amazon.Lambda.Core;
8+
using Amazon.Lambda.S3Events;
9+
using Amazon.S3;
10+
using Amazon.S3.Util;
11+
12+
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
13+
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
14+
15+
namespace ImageResize;
16+
17+
public class Function
18+
{
19+
IAmazonS3 S3Client { get; set; }
20+
21+
22+
/// <summary>
23+
/// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment
24+
/// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the
25+
/// region the Lambda function is executed in.
26+
/// </summary>
27+
public Function()
28+
{
29+
S3Client = new AmazonS3Client();
30+
}
31+
32+
/// <summary>
33+
/// Constructs an instance with a preconfigured S3 client. This can be used for testing outside of the Lambda environment.
34+
/// </summary>
35+
/// <param name="s3Client"></param>
36+
public Function(IAmazonS3 s3Client)
37+
{
38+
this.S3Client = s3Client;
39+
}
40+
41+
public async Task<string> FunctionHandler(S3Event evnt, ILambdaContext context)
42+
{
43+
string[] fileExtensions = new string[] { ".jpg", ".jpeg" };
44+
var s3Event = evnt.Records?[0].S3;
45+
if (s3Event == null)
46+
{
47+
return null;
48+
}
49+
50+
try
51+
{
52+
foreach (var record in evnt.Records)
53+
{
54+
LambdaLogger.Log("----> File: " + record.S3.Object.Key);
55+
if (!fileExtensions.Contains(Path.GetExtension(record.S3.Object.Key).ToLower()))
56+
{
57+
LambdaLogger.Log("File Extension is not supported - " + s3Event.Object.Key);
58+
continue;
59+
}
60+
61+
string suffix = Path.GetExtension(record.S3.Object.Key).ToLower();
62+
Stream imageStream = new MemoryStream();
63+
using (var objectResponse = await S3Client.GetObjectAsync(record.S3.Bucket.Name, record.S3.Object.Key))
64+
{
65+
using (Stream responseStream = objectResponse.ResponseStream)
66+
{
67+
using (var image = Image.Load(responseStream))
68+
{
69+
// Create B&W thumbnail
70+
image.Mutate(ctx => ctx.Grayscale().Resize(200, 200));
71+
image.Save(imageStream, new JpegEncoder());
72+
imageStream.Seek(0, SeekOrigin.Begin);
73+
}
74+
}
75+
}
76+
77+
// Creating a new S3 ObjectKey for the thumbnails
78+
string thumbnailObjectKey = null;
79+
int endSlash = record.S3.Object.Key.ToLower().LastIndexOf("/");
80+
if (endSlash > 0)
81+
{
82+
string S3ObjectName = record.S3.Object.Key.ToLower().Substring(endSlash + 1);
83+
int beginSlash = 0;
84+
if (endSlash > 0)
85+
{
86+
beginSlash = record.S3.Object.Key.ToLower().Substring(0, endSlash - 1).LastIndexOf("/");
87+
if (beginSlash > 0)
88+
{
89+
thumbnailObjectKey =
90+
record.S3.Object.Key.ToLower().Substring(0, beginSlash) +
91+
"thumbnails/" +
92+
S3ObjectName;
93+
}
94+
else
95+
{
96+
thumbnailObjectKey = "thumbnails/" + S3ObjectName;
97+
}
98+
}
99+
}
100+
else
101+
{
102+
thumbnailObjectKey = "thumbnails/" + record.S3.Object.Key.ToLower();
103+
}
104+
105+
LambdaLogger.Log("----> Thumbnail file Key: " + thumbnailObjectKey);
106+
var destinationBucket = Environment.GetEnvironmentVariable("DESTINATION_BUCKET_NAME");
107+
await S3Client.PutObjectAsync(new PutObjectRequest
108+
{
109+
BucketName = destinationBucket,
110+
Key = thumbnailObjectKey,
111+
InputStream = imageStream
112+
});
113+
}
114+
115+
LambdaLogger.Log("Processed " + evnt.Records.Count.ToString());
116+
117+
return null;
118+
}
119+
catch (Exception e)
120+
{
121+
context.Logger.LogLine($"Error getting object {s3Event.Object.Key} from bucket {s3Event.Bucket.Name}");
122+
context.Logger.LogLine($"Make sure they exist and your bucket is in the same region as this function");
123+
context.Logger.LogLine(e.Message);
124+
context.Logger.LogLine(e.StackTrace);
125+
throw;
126+
}
127+
}
128+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net6.0</TargetFramework>
4+
<ImplicitUsings>enable</ImplicitUsings>
5+
<Nullable>enable</Nullable>
6+
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
7+
<AWSProjectType>Lambda</AWSProjectType>
8+
<!-- This property makes the build directory similar to a publish directory and helps the AWS .NET Lambda Mock Test Tool find project dependencies. -->
9+
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
10+
<!-- Generate ready to run images during publishing to improve cold start time. -->
11+
<PublishReadyToRun>true</PublishReadyToRun>
12+
</PropertyGroup>
13+
<ItemGroup>
14+
<PackageReference Include="Amazon.Lambda.Core" Version="2.1.0" />
15+
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.3.1" />
16+
<PackageReference Include="Amazon.Lambda.S3Events" Version="3.0.0" />
17+
<PackageReference Include="AWSSDK.S3" Version="3.7.104.2" />
18+
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
19+
</ItemGroup>
20+
</Project>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"profiles": {
3+
"Mock Lambda Test Tool": {
4+
"commandName": "Executable",
5+
"commandLineArgs": "--port 5050",
6+
"workingDirectory": ".\\bin\\$(Configuration)\\net6.0",
7+
"executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe"
8+
}
9+
}
10+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
{
3+
"Information" : [
4+
"This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
5+
"To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",
6+
"dotnet lambda help",
7+
"All the command line options for the Lambda command can be specified in this file."
8+
],
9+
"profile" : "default",
10+
"region" : "us-east-1",
11+
"configuration" : "Release",
12+
"function-runtime" : "dotnet6",
13+
"function-memory-size" : 256,
14+
"function-timeout" : 30,
15+
"function-handler" : "ImageResize::ImageResize.Function::FunctionHandler",
16+
"framework" : "net6.0",
17+
"function-name" : "ImageResize",
18+
"package-type" : "Zip",
19+
"function-role" : "arn:aws:iam::595982400875:role/ImageResizeLambdaRole",
20+
"function-architecture" : "x86_64",
21+
"function-subnets" : "",
22+
"function-security-groups" : "",
23+
"tracing-mode" : "PassThrough",
24+
"environment-variables" : "",
25+
"image-tag" : ""
26+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# AWS Amazon S3 to AWS Lambda - Create a Lambda function that resizes images uploaded to S3
2+
3+
The SAM template deploys a .NET 6 Lambda function, an S3 bucket and the IAM resources required to run the application. A Lambda function consumes <code>ObjectCreated</code> events from an Amazon S3 bucket. The function code checks the uploaded file is an image and creates a thumbnail version of the image in the same bucket.
4+
5+
Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/s3-lambda-dotnet](https://serverlessland.com/patterns/s3-lambda-dotnet)
6+
7+
Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.
8+
9+
## Requirements
10+
11+
* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
12+
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
13+
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
14+
* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed
15+
* [.Net 6.0](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
16+
* [Docker](https://docs.docker.com/get-docker/) installed and running
17+
18+
## Deployment Instructions
19+
20+
1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
21+
```
22+
git clone https://github.com/aws-samples/serverless-patterns
23+
```
24+
1. Change directory to the pattern directory:
25+
```
26+
cd s3-lambda-dotnet
27+
```
28+
1. From the command line, use AWS SAM to build and deploy the AWS resources for the pattern as specified in the template.yml file:
29+
```
30+
sam build
31+
sam deploy --guided
32+
```
33+
1. During the prompts:
34+
* Enter a stack name
35+
* Enter the desired AWS Region
36+
* Allow SAM CLI to create IAM roles with the required permissions.
37+
38+
Once you have run `sam deploy -guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults.
39+
40+
1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing.
41+
42+
## How it works
43+
44+
* Use the AWS CLI upload an image to S3
45+
* If the object is a .jpeg in the source bucket, the code creates a thumbnail and saves it to the destination bucket in a new folder, /thumbnails.
46+
* The code assumes that the destination bucket exists and is defined in the `template.yaml` file
47+
48+
==============================================
49+
50+
## Testing
51+
52+
Run the following S3 CLI command to upload an image to the S3 bucket. Note, you must edit the {SourceBucketName} placeholder with the name of the S3 Bucket. This is provided in the stack outputs.
53+
54+
```bash
55+
aws s3 cp './images/example.jpeg' s3://{BucketName}/example.jpeg
56+
```
57+
58+
Run the following command to check that a new thumbnails folder has been created with a new version of the image.
59+
60+
```bash
61+
aws s3 ls s3://{BucketName}/thumbnails
62+
```
63+
64+
## Cleanup
65+
66+
1. Delete the stack
67+
```bash
68+
aws cloudformation delete-stack --stack-name STACK_NAME
69+
```
70+
1. Confirm the stack has been deleted
71+
```bash
72+
aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus"
73+
```
74+
----
75+
Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
76+
77+
SPDX-License-Identifier: MIT-0
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"Records": [
3+
{
4+
"eventVersion": "2.1",
5+
"eventSource": "aws:s3",
6+
"awsRegion": "us-east-1",
7+
"eventTime": "2024-07-03T19:37:27.192Z",
8+
"eventName": "ObjectCreated:Put",
9+
"userIdentity": {
10+
"principalId": "AWS:AIDAINPONIXQXHT3IKHL2"
11+
},
12+
"requestParameters": {
13+
"sourceIPAddress": "205.255.255.255"
14+
},
15+
"responseElements": {
16+
"x-amz-request-id": "D82B88E5F771F645",
17+
"x-amz-id-2": "vlR7PnpV2Ce81l0PRw6jlUpck7Jo5ZsQjryTjKlc5aLWGVHPZLj5NeC6qMa0emYBDXOo6QBU0Wo="
18+
},
19+
"s3": {
20+
"s3SchemaVersion": "1.0",
21+
"configurationId": "828aa6fc-f7b5-4305-8584-487c791949c1",
22+
"bucket": {
23+
"name": "<provide-source-bucket-name-here>",
24+
"ownerIdentity": {
25+
"principalId": "A3I5XTEXAMAI3E"
26+
},
27+
"arn": "arn:aws:s3:::lambda-artifacts-deafc19498e3f2df"
28+
},
29+
"object": {
30+
"key": "<provide-object-key-here>",
31+
"size": 1305107,
32+
"eTag": "b21b84d653bb07b05b1e6b33684dc11b",
33+
"sequencer": "0C0F6F405D6ED209E1"
34+
}
35+
}
36+
}
37+
]
38+
}
307 KB
Loading

0 commit comments

Comments
 (0)