Skip to content

Commit 537266a

Browse files
authored
Add KeyDecoded property to get the S3 object key url decoded (#2261)
1 parent 45c3021 commit 537266a

4 files changed

Lines changed: 91 additions & 31 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Projects": [
3+
{
4+
"Name": "Amazon.Lambda.S3Events",
5+
"Type": "Patch",
6+
"ChangelogMessages": [
7+
"Add KeyDecoded property to get the S3 object key url decoded"
8+
]
9+
}
10+
]
11+
}

Libraries/src/Amazon.Lambda.S3Events/S3Event.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,22 @@ public class S3ObjectEntity
5757
{
5858
/// <summary>
5959
/// Gets and sets the key for the object stored in S3.
60+
/// <para>
61+
/// Note: S3 events sent to Lambda will have the object key in the JSON document url encoded. For example if the object key name contains a space character it will be represented as +.
62+
/// This property returns the object key exactly as it is received in the S3 event including being url encoded. To get the key decoded use the <see cref="KeyDecoded"/> property.
63+
/// </para>
6064
/// </summary>
6165
public string Key { get; set; }
6266

67+
/// <summary>
68+
/// Gets and the url decoded key for the object stored in S3.
69+
/// <para>
70+
/// Note: S3 events sent to Lambda will have the object key in the JSON document url encoded. For example if the object key name contains a space character it will be represented as +.
71+
/// This read only property returns the value of <see cref="Key"/> property url decoded.
72+
/// </para>
73+
/// </summary>
74+
public string KeyDecoded => System.Net.WebUtility.UrlDecode(Key);
75+
6376
/// <summary>
6477
/// Gets and sets the size of the object in S3.
6578
/// </summary>
@@ -224,4 +237,4 @@ public class S3EventNotificationRecord
224237
public S3GlacierEventDataEntity GlacierEventData { get; set; }
225238
}
226239
}
227-
}
240+
}

Libraries/test/EventsTests.Shared/EventTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ public void S3PutTest(Type serializerType)
256256
{
257257
var s3Event = serializer.Deserialize<S3Event>(fileStream);
258258

259-
Assert.Equal(s3Event.Records.Count, 1);
259+
Assert.Equal(s3Event.Records.Count, 2);
260260
var record = s3Event.Records[0];
261261
Assert.Equal(record.EventVersion, "2.0");
262262
Assert.Equal(record.EventTime.ToUniversalTime(), DateTime.Parse("1970-01-01T00:00:00.000Z").ToUniversalTime());
@@ -276,6 +276,9 @@ public void S3PutTest(Type serializerType)
276276
Assert.Equal(record.UserIdentity.PrincipalId, "EXAMPLE");
277277
Assert.Equal(record.EventSource, "aws:s3");
278278

279+
// In the events file the key is New+File.jpg simulating the key being url encoded.
280+
Assert.Equal("New File.jpg", s3Event.Records[1].S3.Object.KeyDecoded);
281+
279282
Handle(s3Event);
280283
}
281284
}
Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,69 @@
1-
{
1+
{
22
"Records": [
33
{
4-
"eventVersion": "2.0",
5-
"eventTime": "1970-01-01T00:00:00.000Z",
6-
"requestParameters": { "sourceIPAddress": "127.0.0.1" },
7-
"s3": {
8-
"configurationId": "testConfigRule",
9-
"object": {
10-
"eTag": "0123456789abcdef0123456789abcdef",
11-
"sequencer": "0A1B2C3D4E5F678901",
12-
"key": "HappyFace.jpg",
13-
"size": 1024
4+
"eventVersion": "2.0",
5+
"eventTime": "1970-01-01T00:00:00.000Z",
6+
"requestParameters": { "sourceIPAddress": "127.0.0.1" },
7+
"s3": {
8+
"configurationId": "testConfigRule",
9+
"object": {
10+
"eTag": "0123456789abcdef0123456789abcdef",
11+
"sequencer": "0A1B2C3D4E5F678901",
12+
"key": "HappyFace.jpg",
13+
"size": 1024
14+
},
15+
"bucket": {
16+
"arn": "arn:aws:s3:::mybucket",
17+
"name": "sourcebucket",
18+
"ownerIdentity": {
19+
"principalId": "EXAMPLE"
20+
}
21+
},
22+
"s3SchemaVersion": "1.0"
1423
},
15-
"bucket": {
16-
"arn": "arn:aws:s3:::mybucket",
17-
"name": "sourcebucket",
18-
"ownerIdentity": {
24+
"responseElements": {
25+
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH",
26+
"x-amz-request-id": "EXAMPLE123456789"
27+
},
28+
"awsRegion": "us-east-1",
29+
"eventName": "ObjectCreated:Put",
30+
"userIdentity": {
31+
"principalId": "EXAMPLE"
32+
},
33+
"eventSource": "aws:s3"
34+
},
35+
{
36+
"eventVersion": "2.0",
37+
"eventTime": "1970-01-01T00:00:00.000Z",
38+
"requestParameters": { "sourceIPAddress": "127.0.0.1" },
39+
"s3": {
40+
"configurationId": "testConfigRule",
41+
"object": {
42+
"eTag": "0123456789abcdef0123456789abcdef",
43+
"sequencer": "0A1B2C3D4E5F678901",
44+
"key": "New+File.jpg",
45+
"size": 1024
46+
},
47+
"bucket": {
48+
"arn": "arn:aws:s3:::mybucket",
49+
"name": "sourcebucket",
50+
"ownerIdentity": {
51+
"principalId": "EXAMPLE"
52+
}
53+
},
54+
"s3SchemaVersion": "1.0"
55+
},
56+
"responseElements": {
57+
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH",
58+
"x-amz-request-id": "EXAMPLE123456789"
59+
},
60+
"awsRegion": "us-east-1",
61+
"eventName": "ObjectCreated:Put",
62+
"userIdentity": {
1963
"principalId": "EXAMPLE"
20-
}
2164
},
22-
"s3SchemaVersion": "1.0"
23-
},
24-
"responseElements": {
25-
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH",
26-
"x-amz-request-id": "EXAMPLE123456789"
27-
},
28-
"awsRegion": "us-east-1",
29-
"eventName": "ObjectCreated:Put",
30-
"userIdentity": {
31-
"principalId": "EXAMPLE"
32-
},
33-
"eventSource": "aws:s3"
65+
"eventSource": "aws:s3"
3466
}
67+
3568
]
36-
}
69+
}

0 commit comments

Comments
 (0)