S3 Object Lambda 접근 지점용 Lambda 함수 작성
S3 Object Lambda 접근 지점용 Lambda 함수 작성 (Writing Lambda functions for S3 Object Lambda Access Points)
이 절에서는 Amazon S3 Object Lambda 접근 지점과 함께 사용할 AWS Lambda 함수를 작성하는 방법을 자세히 설명해요. S3 Object Lambda 작업 전체 과정을 처음부터 끝까지 배우고 싶다면 다음 자습서를 참고하세요.
- S3 Object Lambda로 애플리케이션용 데이터 변환 자습서
- S3 Object Lambda와 Amazon Comprehend로 PII 데이터 감지 및 편집 자습서
- 검색 시 이미지에 동적으로 워터마크를 입히는 S3 Object Lambda 사용 자습서
참고 2025년 11월 7일부터 S3 Object Lambda는 현재 이 서비스를 사용 중인 기존 고객과 일부 AWS 파트너 네트워크(APN) 파트너만 이용할 수 있어요. S3 Object Lambda와 비슷한 기능이 필요하다면 Amazon S3 Object Lambda 가용성 변경 문서를 확인해 주세요.
출처: 문서
본문
항목
- Lambda에서 GetObject 요청 작업
- Lambda에서 HeadObject 요청 작업
- Lambda에서 ListObjects 요청 작업
- Lambda에서 ListObjectsV2 요청 작업
- 이벤트 컨텍스트 형식 및 사용법
- Range 및 partNumber 헤더 사용
Lambda에서 GetObject 요청 작업
이 절은 Object Lambda 접근 지점이 GetObject에 대해 Lambda 함수를 호출하도록 구성되어 있다고 가정해요. S3 Object Lambda에는 Lambda 함수가 GetObject 호출자에게 사용자 지정 데이터와 응답 헤더를 제공할 수 있게 해 주는 Amazon S3 API 작업인 WriteGetObjectResponse가 포함돼 있어요.
WriteGetObjectResponse는 처리 요구에 따라 상태 코드, 응답 헤더, 응답 본문을 폭넓게 제어할 수 있게 해 줘요. WriteGetObjectResponse를 사용해 변환된 객체 전체, 변환된 객체의 일부분, 또는 애플리케이션 컨텍스트에 따른 다른 응답으로 응답할 수 있어요. 다음 절에서는 WriteGetObjectResponse API 작업을 사용하는 독특한 예제를 보여줘요.
- 예제 1: HTTP 상태 코드 403(Forbidden)으로 응답
- 예제 2: 변환된 이미지로 응답
- 예제 3: 압축된 콘텐츠 스트리밍
예제 1: HTTP 상태 코드 403(Forbidden)으로 응답
WriteGetObjectResponse를 사용해 객체의 내용에 따라 HTTP 상태 코드 403(Forbidden)으로 응답할 수 있어요.
Java
package com.amazon.s3.objectlambda;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example1
{
public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception
{
S3Client s3Client = S3Client.builder().build();
// Check to see if the request contains all of the necessary information.
// If it does not, send a 4XX response and a custom error code and message.
// Otherwise, retrieve the object from S3 and stream it
// to the client unchanged.
var tokenIsNotPresent = !event.getUserRequest().getHeaders().containsKey("requiredToken");
if (tokenIsNotPresent)
{
s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
.requestRoute(event.outputRoute())
.requestToken(event.outputToken())
.statusCode(403)
.contentLength(0L)
.errorCode("MissingRequiredToken")
.errorMessage("The required token was not present in the request.")
.build(),
RequestBody.fromInputStream(new ByteArrayInputStream(new byte[0]), 0L));
return;
}
// Prepare the presigned URL for use and make the request to S3.
HttpClient httpClient = HttpClient.newBuilder().build();
var presignedResponse = httpClient.send(
HttpRequest.newBuilder(new URI(event.inputS3Url())).GET().build(),
HttpResponse.BodyHandlers.ofInputStream());
// Stream the original bytes back to the caller.
s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
.requestRoute(event.outputRoute())
.requestToken(event.outputToken())
.build(),
RequestBody.fromInputStream(presignedResponse.body(),
presignedResponse.headers().firstValueAsLong("content-length").orElse(-1L)));
}
}
Python
import boto3
import requests
def handler(event, context):
s3 = boto3.client('s3')
"""
Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
should be delivered and contains a presigned URL in 'inputS3Url' where we can download the requested object from.
The 'userRequest' object has information related to the user who made this 'GetObject' request to
S3 Object Lambda.
"""
get_context = event["getObjectContext"]
user_request_headers = event["userRequest"]["headers"]
route = get_context["outputRoute"]
token = get_context["outputToken"]
s3_url = get_context["inputS3Url"]
# Check for the presence of a 'CustomHeader' header and deny or allow based on that header.
is_token_present = "SuperSecretToken" in user_request_headers
if is_token_present:
# If the user presented our custom 'SuperSecretToken' header, we send the requested object back to the user.
response = requests.get(s3_url)
s3.write_get_object_response(RequestRoute=route, RequestToken=token, Body=response.content)
else:
# If the token is not present, we send an error back to the user.
s3.write_get_object_response(RequestRoute=route, RequestToken=token, StatusCode=403,
ErrorCode="NoSuperSecretTokenFound", ErrorMessage="The request was not secret enough.")
# Gracefully exit the Lambda function.
return {
'status_code': 200 }
Node.js
const {S3} = require('aws-sdk');
const axios = require('axios').default;
exports.handler = async (event) =>
{
const s3 = new S3();
// Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
// should be delivered and contains a presigned URL in 'inputS3Url' where we can download the requested object from.
// The 'userRequest' object has information related to the user who made this 'GetObject' request to S3 Object Lambda.
const {userRequest, getObjectContext} = event;
const {outputRoute, outputToken, inputS3Url} = getObjectContext;
// Check for the presence of a 'CustomHeader' header and deny or allow based on that header.
const isTokenPresent = Object
.keys(userRequest.headers)
.includes("SuperSecretToken");
if (!isTokenPresent)
{
// If the token is not present, we send an error back to the user. The 'await' in front of the request
// indicates that we want to wait for this request to finish sending before moving on.
await s3.writeGetObjectResponse(
{
RequestRoute: outputRoute,
RequestToken: outputToken,
StatusCode: 403,
ErrorCode: "NoSuperSecretTokenFound",
ErrorMessage: "The request was not secret enough.",
}).promise();
} else
{
// If the user presented our custom 'SuperSecretToken' header, we send the requested object back to the user.
// Again, note the presence of 'await'.
const presignedResponse = await axios.get(inputS3Url);
await s3.writeGetObjectResponse(
{
RequestRoute: outputRoute,
RequestToken: outputToken,
Body: presignedResponse.data,
}).promise();
}
// Gracefully exit the Lambda function.
return {
statusCode: 200 };
}
예제 2: 변환된 이미지로 응답
이미지 변환을 수행할 때 처리 전에 소스 객체의 모든 바이트가 필요할 수 있어요. 이 경우 WriteGetObjectResponse 요청은 전체 객체를 한 번의 호출로 요청하는 애플리케이션에 반환해요.
Java
package com.amazon.s3.objectlambda;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.Image;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example2V2
{
private static final int HEIGHT = 250;
private static final int WIDTH = 250;
public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception
{
S3Client s3Client = S3Client.builder().build();
HttpClient httpClient = HttpClient.newBuilder().build();
// Prepare the presigned URL for use and make the request to S3.
var presignedResponse = httpClient.send(
HttpRequest.newBuilder(new URI(event.inputS3Url())).GET().build(),
HttpResponse.BodyHandlers.ofInputStream());
// The entire image is loaded into memory here so that we can resize it.
// Once the resizing is completed, we write the bytes into the body
// of the WriteGetObjectResponse request.
var originalImage = ImageIO.read(presignedResponse.body());
var resizingImage = originalImage.getScaledInstance(WIDTH, HEIGHT, Image.SCALE_DEFAULT);
var resizedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
resizedImage.createGraphics().drawImage(resizingImage, 0, 0, WIDTH, HEIGHT, null);
var baos = new ByteArrayOutputStream();
ImageIO.write(resizedImage, "png", baos);
// Stream the bytes back to the caller.
s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
.requestRoute(event.outputRoute())
.requestToken(event.outputToken())
.build(), RequestBody.fromBytes(baos.toByteArray()));
}
}
Python
import boto3
import requests
import io
from PIL import Image
def handler(event, context):
"""
Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
The 'userRequest' object has information related to the user who made this 'GetObject' request to
S3 Object Lambda.
"""
get_context = event["getObjectContext"]
route = get_context["outputRoute"]
token = get_context["outputToken"]
s3_url = get_context["inputS3Url"]
"""
In this case, we're resizing .png images that are stored in S3 and are accessible through the presigned URL
'inputS3Url'.
"""
image_request = requests.get(s3_url)
image = Image.open(io.BytesIO(image_request.content))
image.thumbnail((256,256), Image.ANTIALIAS)
transformed = io.BytesIO()
image.save(transformed, "png")
# Send the resized image back to the client.
s3 = boto3.client('s3')
s3.write_get_object_response(Body=transformed.getvalue(), RequestRoute=route, RequestToken=token)
# Gracefully exit the Lambda function.
return {
'status_code': 200 }
Node.js
const {S3} = require('aws-sdk');
const axios = require('axios').default;
const sharp = require('sharp');
exports.handler = async (event) =>
{
const s3 = new S3();
// Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
// should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
const {getObjectContext} = event;
const {outputRoute, outputToken, inputS3Url} = getObjectContext;
// In this case, we're resizing .png images that are stored in S3 and are accessible through the presigned URL
// 'inputS3Url'.
const {data} = await axios.get(inputS3Url,
{
responseType: 'arraybuffer' });
// Resize the image.
const resized = await sharp(data)
.resize(
{
width: 256, height: 256 })
.toBuffer();
// Send the resized image back to the client.
await s3.writeGetObjectResponse(
{
RequestRoute: outputRoute,
RequestToken: outputToken,
Body: resized,
}).promise();
// Gracefully exit the Lambda function.
return {
statusCode: 200 };
}
예제 3: 압축된 콘텐츠 스트리밍
객체를 압축할 때 압축된 데이터는 점진적으로 생성돼요. 따라서 WriteGetObjectResponse 요청을 사용해 준비되는 대로 압축된 데이터를 반환할 수 있어요. 이 예제에서 볼 수 있듯이 완료된 변환의 길이를 알 필요가 없어요.
Java
package com.amazon.s3.objectlambda;
import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent;
import com.amazonaws.services.lambda.runtime.Context;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example3
{
public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception
{
S3Client s3Client = S3Client.builder().build();
HttpClient httpClient = HttpClient.newBuilder().build();
// Request the original object from S3.
var presignedResponse = httpClient.send(
HttpRequest.newBuilder(new URI(event.inputS3Url())).GET().build(),
HttpResponse.BodyHandlers.ofInputStream());
// Consume the incoming response body from the presigned request,
// apply our transformation on that data, and emit the transformed bytes
// into the body of the WriteGetObjectResponse request as soon as they're ready.
// This example compresses the data from S3, but any processing pertinent
// to your application can be performed here.
var bodyStream = new GZIPCompressingInputStream(presignedResponse.body());
// Stream the bytes back to the caller.
s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
.requestRoute(event.outputRoute())
.requestToken(event.outputToken())
.build(),
RequestBody.fromInputStream(bodyStream,
presignedResponse.headers().firstValueAsLong("content-length").orElse(-1L)));
}
}
Python
import boto3
import requests
import zlib
from botocore.config import Config
"""
A helper class to work with content iterators. Takes an interator and compresses the bytes that come from it. It
implements 'read' and '__iter__' so that the SDK can stream the response.
"""
class Compress:
def __init__(self, content_iter):
self.content = content_iter
self.compressed_obj = zlib.compressobj()
def read(self, _size):
for data in self.__iter__():
return data
def __iter__(self):
while True:
data = next(self.content)
chunk = self.compressed_obj.compress(data)
if not chunk:
break
yield chunk
yield self.compressed_obj.flush()
def handler(event, context):
"""
Setting the 'payload_signing_enabled' property to False allows us to send a streamed response back to the client.
in this scenario, a streamed response means that the bytes are not buffered into memory as we're compressing them,
but instead are sent straight to the user.
"""
my_config = Config(
region_name='eu-west-1',
signature_version='s3v4',
s3={
"payload_signing_enabled": False
}
)
s3 = boto3.client('s3', config=my_config)
"""
Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
The 'userRequest' object has information related to the user who made this 'GetObject' request to S3 Object Lambda.
"""
get_context = event["getObjectContext"]
route = get_context["outputRoute"]
token = get_context["outputToken"]
s3_url = get_context["inputS3Url"]
# Compress the 'get' request stream.
with requests.get(s3_url, stream=True) as r:
compressed = Compress(r.iter_content())
# Send the stream back to the client.
s3.write_get_object_response(Body=compressed, RequestRoute=route, RequestToken=token, ContentType="text/plain",
ContentEncoding="gzip")
# Gracefully exit the Lambda function.
return {
'status_code': 200}
Node.js
const {S3} = require('aws-sdk');
const axios = require('axios').default;
const zlib = require('zlib');
exports.handler = async (event) =>
{
const s3 = new S3();
// Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
// should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
const {getObjectContext} = event;
const {outputRoute, outputToken, inputS3Url} = getObjectContext;
// Download the object from S3 and process it as a stream, because it might be a huge object and we don't want to
// buffer it in memory. Note the use of 'await' because we want to wait for 'writeGetObjectResponse' to finish
// before we can exit the Lambda function.
await axios(
{
method: 'GET',
url: inputS3Url,
responseType: 'stream',
}).then(
// Gzip the stream.
response => response.data.pipe(zlib.createGzip())
).then(
// Finally send the gzip-ed stream back to the client.
stream => s3.writeGetObjectResponse(
{
RequestRoute: outputRoute,
RequestToken: outputToken,
Body: stream,
ContentType: "text/plain",
ContentEncoding: "gzip",
}).promise()
);
// Gracefully exit the Lambda function.
return {
statusCode: 200 };
}
참고 S3 Object Lambda는
WriteGetObjectResponse요청을 통해 호출자에게 완전한 응답을 보내는 데 최대 60초를 허용하지만, 실제 사용 가능한 시간은 더 짧을 수 있어요. 예를 들어 Lambda 함수 제한 시간이 60초보다 짧을 수 있어요. 다른 경우 호출자가 더 엄격한 제한 시간을 가질 수도 있어요.
원래 호출자가 HTTP 상태 코드 500(Internal Server Error)이 아닌 응답을 받으려면 WriteGetObjectResponse 호출이 완료되어야 해요. Lambda 함수가 WriteGetObjectResponse API 작업이 호출되기 전에 예외가 발생하든 그렇지 않든 반환되면 원래 호출자는 500(Internal Server Error) 응답을 받아요. 응답을 완료하는 데 걸리는 시간 동안 발생한 예외는 호출자에게 잘린 응답을 초래해요. Lambda 함수가 WriteGetObjectResponse API 호출로부터 HTTP 상태 코드 200(OK) 응답을 받으면 원래 호출자에게 완전한 요청이 전송된 거예요. Lambda 함수의 응답은 예외가 발생했는지 여부와 관계없이 S3 Object Lambda가 무시해요.
WriteGetObjectResponse API 작업을 호출할 때 Amazon S3는 이벤트 컨텍스트의 경로(route)와 요청 토큰을 요구해요. 자세한 내용은 이벤트 컨텍스트 형식 및 사용법을 참고하세요.
WriteGetObjectResult 응답을 원래 호출자와 연결하려면 경로와 요청 토큰 파라미터가 필요해요. 500(Internal Server Error) 응답을 재시도하는 것은 항상 적절하지만, 요청 토큰은 일회용이므로 이후 사용 시도는 HTTP 상태 코드 400(Bad Request) 응답을 초래할 수 있어요. 경로와 요청 토큰을 사용한 WriteGetObjectResponse 호출은 호출된 Lambda 함수에서 수행할 필요는 없지만, 같은 계정의 자격 증명이 수행해야 해요. 호출은 Lambda 함수 실행이 끝나기 전에 완료되어야 해요.
Lambda에서 HeadObject 요청 작업
이 절은 Object Lambda 접근 지점이 HeadObject에 대해 Lambda 함수를 호출하도록 구성되어 있다고 가정해요. Lambda는 headObjectContext라는 키가 포함된 JSON 페이로드를 받아요. 컨텍스트 안에는 inputS3Url이라는 단일 속성이 있는데, 이것은 HeadObject에 대한 지원 접근 지점의 사전 서명 URL이에요.
사전 서명 URL은 지정된 경우 다음 속성을 포함해요.
versionId(쿼리 파라미터에 있음)requestPayer(x-amz-request-payer헤더에 있음)expectedBucketOwner(x-amz-expected-bucket-owner헤더에 있음)
다른 속성은 사전 서명되지 않으므로 포함되지 않아요. 헤더로 전송되는 서명되지 않은 옵션은 userRequest 헤더에서 찾은 사전 서명 URL을 호출할 때 요청에 수동으로 추가할 수 있어요. HeadObject에는 서버 측 암호화 옵션이 지원되지 않아요.
요청 구문 URI 파라미터는 Amazon Simple Storage Service API Reference의 HeadObject를 참고하세요.
다음 예제는 HeadObject에 대한 Lambda JSON 입력 페이로드를 보여줘요.
{
"xAmzRequestId": "requestId",
"**headObjectContext**":{
"**inputS3Url**": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=<snip>"
},
"configuration":{
"accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
"supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
"payload": "{}"
},
"userRequest":{
"url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
"headers":{
"Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
"Accept-Encoding": "identity",
"X-Amz-Content-SHA256": "e3b0c44298fc1example"
}
},
"userIdentity":{
"type": "AssumedRole",
"principalId": "principalId",
"arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",
"accountId": "111122223333",
"accessKeyId": "accessKeyId",
"sessionContext":{
"attributes":{
"mfaAuthenticated": "false",
"creationDate": "Wed Mar 10 23:41:52 UTC 2021"
},
"sessionIssuer":{
"type": "Role",
"principalId": "principalId",
"arn": "arn:aws:iam::111122223333:role/Admin",
"accountId": "111122223333",
"userName": "Admin"
}
}
},
"protocolVersion": "1.00"
}
Lambda 함수는 HeadObject 호출에 대해 반환될 헤더와 값을 포함한 JSON 객체를 반환해야 해요.
다음 예제는 HeadObject에 대한 Lambda 응답 JSON의 구조를 보여줘요.
{
"statusCode": <number>; // Required
"errorCode": <string>;
"errorMessage": <string>;
"headers":{
"Accept-Ranges": <string>,
"x-amz-archive-status": <string>,
"x-amz-server-side-encryption-bucket-key-enabled": <boolean>,
"Cache-Control": <string>,
"Content-Disposition": <string>,
"Content-Encoding": <string>,
"Content-Language": <string>,
"Content-Length": <number>, // Required
"Content-Type": <string>,
"x-amz-delete-marker": <boolean>,
"ETag": <string>,
"Expires": <string>,
"x-amz-expiration": <string>,
"Last-Modified": <string>,
"x-amz-missing-meta": <number>,
"x-amz-object-lock-mode": <string>,
"x-amz-object-lock-legal-hold": <string>,
"x-amz-object-lock-retain-until-date": <string>,
"x-amz-mp-parts-count": <number>,
"x-amz-replication-status": <string>,
"x-amz-request-charged": <string>,
"x-amz-restore": <string>,
"x-amz-server-side-encryption": <string>,
"x-amz-server-side-encryption-customer-algorithm": <string>,
"x-amz-server-side-encryption-aws-kms-key-id": <string>,
"x-amz-server-side-encryption-customer-key-MD5": <string>,
"x-amz-storage-class": <string>,
"x-amz-tagging-count": <number>,
"x-amz-version-id": <string>,
<x-amz-meta-headers>: <string>, // user-defined metadata
"x-amz-meta-meta1": <string>, // example of the user-defined metadata header, it will need the x-amz-meta prefix
"x-amz-meta-meta2": <string>
...
};
}
다음 예제는 사전 서명 URL을 사용해 JSON을 반환하기 전에 필요에 따라 헤더 값을 수정해 응답을 채우는 방법을 보여줘요.
Python
import requests
def lambda_handler(event, context):
print(event)
# Extract the presigned URL from the input.
s3_url = event["headObjectContext"]["inputS3Url"]
# Get the head of the object from S3.
response = requests.head(s3_url)
# Return the error to S3 Object Lambda (if applicable).
if (response.status_code >= 400):
return {
"statusCode": response.status_code,
"errorCode": "RequestFailure",
"errorMessage": "Request to S3 failed"
}
# Store the headers in a dictionary.
response_headers = dict(response.headers)
# This obscures Content-Type in a transformation, it is optional to add
response_headers["Content-Type"] = ""
# Return the headers to S3 Object Lambda.
return {
"statusCode": response.status_code,
"headers": response_headers
}
Lambda에서 ListObjects 요청 작업
이 절은 Object Lambda 접근 지점이 ListObjects에 대해 Lambda 함수를 호출하도록 구성되어 있다고 가정해요. Lambda는 listObjectsContext라는 새 객체가 포함된 JSON 페이로드를 받아요. listObjectsContext에는 ListObjects에 대한 지원 접근 지점의 사전 서명 URL인 단일 속성 inputS3Url이 포함돼 있어요.
GetObject 및 HeadObject와 달리 사전 서명 URL은 지정된 경우 다음 속성을 포함해요.
- 모든 쿼리 파라미터
requestPayer(x-amz-request-payer헤더에 있음)expectedBucketOwner(x-amz-expected-bucket-owner헤더에 있음)
요청 구문 URI 파라미터는 Amazon Simple Storage Service API Reference의 ListObjects를 참고하세요.
중요 애플리케이션을 개발할 때는 더 새로운 버전인
ListObjectsV2를 사용할 것을 권장해요. 이전 버전과의 호환성을 위해 Amazon S3는ListObjects를 계속 지원해요.
다음 예제는 ListObjects에 대한 Lambda JSON 입력 페이로드를 보여줘요.
{
"xAmzRequestId": "requestId",
"**listObjectsContext**":{
"**inputS3Url**": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/?X-Amz-Security-Token=<snip>",
},
"configuration":{
"accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
"supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
"payload": "{}"
},
"userRequest":{
"url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
"headers":{
"Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
"Accept-Encoding": "identity",
"X-Amz-Content-SHA256": "e3b0c44298fc1example"
}
},
"userIdentity":{
"type": "AssumedRole",
"principalId": "principalId",
"arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",
"accountId": "111122223333",
"accessKeyId": "accessKeyId",
"sessionContext":{
"attributes":{
"mfaAuthenticated": "false",
"creationDate": "Wed Mar 10 23:41:52 UTC 2021"
},
"sessionIssuer":{
"type": "Role",
"principalId": "principalId",
"arn": "arn:aws:iam::111122223333:role/Admin",
"accountId": "111122223333",
"userName": "Admin"
}
}
},
"protocolVersion": "1.00"
}
Lambda 함수는 S3 Object Lambda에서 반환될 상태 코드, 목록 XML 결과 또는 오류 정보를 포함한 JSON 객체를 반환해야 해요. S3 Object Lambda는 listResultXml을 처리하거나 검증하지 않고 ListObjects 호출자에게 전달해요. listBucketResult의 경우 S3 Object Lambda는 특정 속성이 특정 유형이기를 기대하며, 파싱할 수 없으면 예외를 던져요. listResultXml과 listBucketResult는 동시에 제공할 수 없어요.
다음 예제는 사전 서명 URL을 사용해 Amazon S3를 호출하고 오류 확인을 포함해 결과로 응답을 채우는 방법을 보여줘요.
Python
import requests
import xmltodict
def lambda_handler(event, context):
# Extract the presigned URL from the input.
s3_url = event["listObjectsContext"]["inputS3Url"]
# Get the head of the object from Amazon S3.
response = requests.get(s3_url)
# Return the error to S3 Object Lambda (if applicable).
if (response.status_code >= 400):
error = xmltodict.parse(response.content)
return {
"statusCode": response.status_code,
"errorCode": error["Error"]["Code"],
"errorMessage": error["Error"]["Message"]
}
# Store the XML result in a dict.
response_dict = xmltodict.parse(response.content)
# This obscures StorageClass in a transformation, it is optional to add
for item in response_dict['ListBucketResult']['Contents']:
item['StorageClass'] = ""
# Convert back to XML.
listResultXml = xmltodict.unparse(response_dict)
# Create response with listResultXml.
response_with_list_result_xml = {
'statusCode': 200,
'listResultXml': listResultXml
}
# Create response with listBucketResult.
response_dict['ListBucketResult'] = sanitize_response_dict(response_dict['ListBucketResult'])
response_with_list_bucket_result = {
'statusCode': 200,
'listBucketResult': response_dict['ListBucketResult']
}
# Return the list to S3 Object Lambda.
# Can return response_with_list_result_xml or response_with_list_bucket_result
return response_with_list_result_xml
# Converting the response_dict's key to correct casing
def sanitize_response_dict(response_dict: dict):
new_response_dict = dict()
for key, value in response_dict.items():
new_key = key[0].lower() + key[1:] if key != "ID" else 'id'
if type(value) == list:
newlist = []
for element in value:
if type(element) == type(dict()):
element = sanitize_response_dict(element)
newlist.append(element)
value = newlist
elif type(value) == dict:
value = sanitize_response_dict(value)
new_response_dict[new_key] = value
return new_response_dict
다음 예제는 ListObjects에 대한 Lambda 응답 JSON의 구조를 보여줘요.
{
"statusCode": <number>; // Required
"errorCode": <string>;
"errorMessage": <string>;
"listResultXml": <string>; // This can also be Error XML string in case S3 returned error response when calling the pre-signed URL
"listBucketResult":{
// listBucketResult can be provided instead of listResultXml, however they can not both be provided in the JSON response
"name": <string>, // Required for 'listBucketResult'
"prefix": <string>,
"marker": <string>,
"nextMarker": <string>,
"maxKeys": <int>, // Required for 'listBucketResult'
"delimiter": <string>,
"encodingType": <string>
"isTruncated": <boolean>, // Required for 'listBucketResult'
"contents": [
{
"key": <string>, // Required for 'content'
"lastModified": <string>,
"eTag": <string>,
"checksumAlgorithm": <string>, // CRC32, CRC32C, SHA1, SHA256
"size": <int>, // Required for 'content'
"owner":{
"displayName": <string>, // Required for 'owner'
"id": <string>, // Required for 'owner'
},
"storageClass": <string>
},
...
],
"commonPrefixes": [
{
"prefix": <string> // Required for 'commonPrefix'
},
...
],
}
}
Lambda에서 ListObjectsV2 요청 작업
이 절은 Object Lambda 접근 지점이 ListObjectsV2에 대해 Lambda 함수를 호출하도록 구성되어 있다고 가정해요. Lambda는 listObjectsV2Context라는 새 객체가 포함된 JSON 페이로드를 받아요. listObjectsV2Context에는 ListObjectsV2에 대한 지원 접근 지점의 사전 서명 URL인 단일 속성 inputS3Url이 포함돼 있어요.
GetObject 및 HeadObject와 달리 사전 서명 URL은 지정된 경우 다음 속성을 포함해요.
- 모든 쿼리 파라미터
requestPayer(x-amz-request-payer헤더에 있음)expectedBucketOwner(x-amz-expected-bucket-owner헤더에 있음)
요청 구문 URI 파라미터는 Amazon Simple Storage Service API Reference의 ListObjectsV2를 참고하세요.
다음 예제는 ListObjectsV2에 대한 Lambda JSON 입력 페이로드를 보여줘요.
{
"xAmzRequestId": "requestId",
"**listObjectsV2Context**":{
"**inputS3Url**": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/?list-type=2&X-Amz-Security-Token=<snip>",
},
"configuration":{
"accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
"supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
"payload": "{}"
},
"userRequest":{
"url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
"headers":{
"Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
"Accept-Encoding": "identity",
"X-Amz-Content-SHA256": "e3b0c44298fc1example"
}
},
"userIdentity":{
"type": "AssumedRole",
"principalId": "principalId",
"arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",
"accountId": "111122223333",
"accessKeyId": "accessKeyId",
"sessionContext":{
"attributes":{
"mfaAuthenticated": "false",
"creationDate": "Wed Mar 10 23:41:52 UTC 2021"
},
"sessionIssuer":{
"type": "Role",
"principalId": "principalId",
"arn": "arn:aws:iam::111122223333:role/Admin",
"accountId": "111122223333",
"userName": "Admin"
}
}
},
"protocolVersion": "1.00"
}
Lambda 함수는 S3 Object Lambda에서 반환될 상태 코드, 목록 XML 결과 또는 오류 정보를 포함한 JSON 객체를 반환해야 해요. S3 Object Lambda는 listResultXml을 처리하거나 검증하지 않고 ListObjectsV2 호출자에게 전달해요. listBucketResult의 경우 S3 Object Lambda는 특정 속성이 특정 유형이기를 기대하며, 파싱할 수 없으면 예외를 던져요. listResultXml과 listBucketResult는 동시에 제공할 수 없어요.
다음 예제는 사전 서명 URL을 사용해 Amazon S3를 호출하고 오류 확인을 포함해 결과로 응답을 채우는 방법을 보여줘요.
Python
import requests
import xmltodict
def lambda_handler(event, context):
# Extract the presigned URL from the input.
s3_url = event["listObjectsV2Context"]["inputS3Url"]
# Get the head of the object from Amazon S3.
response = requests.get(s3_url)
# Return the error to S3 Object Lambda (if applicable).
if (response.status_code >= 400):
error = xmltodict.parse(response.content)
return {
"statusCode": response.status_code,
"errorCode": error["Error"]["Code"],
"errorMessage": error["Error"]["Message"]
}
# Store the XML result in a dict.
response_dict = xmltodict.parse(response.content)
# This obscures StorageClass in a transformation, it is optional to add
for item in response_dict['ListBucketResult']['Contents']:
item['StorageClass'] = ""
# Convert back to XML.
listResultXml = xmltodict.unparse(response_dict)
# Create response with listResultXml.
response_with_list_result_xml = {
'statusCode': 200,
'listResultXml': listResultXml
}
# Create response with listBucketResult.
response_dict['ListBucketResult'] = sanitize_response_dict(response_dict['ListBucketResult'])
response_with_list_bucket_result = {
'statusCode': 200,
'listBucketResult': response_dict['ListBucketResult']
}
# Return the list to S3 Object Lambda.
# Can return response_with_list_result_xml or response_with_list_bucket_result
return response_with_list_result_xml
# Converting the response_dict's key to correct casing
def sanitize_response_dict(response_dict: dict):
new_response_dict = dict()
for key, value in response_dict.items():
new_key = key[0].lower() + key[1:] if key != "ID" else 'id'
if type(value) == list:
newlist = []
for element in value:
if type(element) == type(dict()):
element = sanitize_response_dict(element)
newlist.append(element)
value = newlist
elif type(value) == dict:
value = sanitize_response_dict(value)
new_response_dict[new_key] = value
return new_response_dict
다음 예제는 ListObjectsV2에 대한 Lambda 응답 JSON의 구조를 보여줘요.
{
"statusCode": <number>; // Required
"errorCode": <string>;
"errorMessage": <string>;
"listResultXml": <string>; // This can also be Error XML string in case S3 returned error response when calling the pre-signed URL
"listBucketResult":{
// listBucketResult can be provided instead of listResultXml, however they can not both be provided in the JSON response
"name": <string>, // Required for 'listBucketResult'
"prefix": <string>,
"startAfter": <string>,
"continuationToken": <string>,
"nextContinuationToken": <string>,
"keyCount": <int>, // Required for 'listBucketResult'
"maxKeys": <int>, // Required for 'listBucketResult'
"delimiter": <string>,
"encodingType": <string>
"isTruncated": <boolean>, // Required for 'listBucketResult'
"contents": [
{
"key": <string>, // Required for 'content'
"lastModified": <string>,
"eTag": <string>,
"checksumAlgorithm": <string>, // CRC32, CRC32C, SHA1, SHA256
"size": <int>, // Required for 'content'
"owner":{
"displayName": <string>, // Required for 'owner'
"id": <string>, // Required for 'owner'
},
"storageClass": <string>
},
...
],
"commonPrefixes": [
{
"prefix": <string> // Required for 'commonPrefix'
},
...
],
}
}