S3 Object Lambda アクセスポイントの Lambda 関数の記述 - Amazon Simple Storage Service

S3 Object Lambda アクセスポイントの Lambda 関数の記述

このセクションでは、Amazon S3 Object Lambda アクセスポイントで使用するための AWS Lambda 関数を記述する方法について詳述します。

S3 Object Lambda の一部のタスクにおける完全なエンドツーエンド手順については、以下を参照してください。

Lambda での GetObject リクエストの使用

このセクションでは、Object Lambda アクセスポイントが GetObject 用に Lambda 関数を呼び出すように設定されていることを前提としています。S3 Object Lambda には、Amazon S3 API オペレーション、WriteGetObjectResponse が含まれています。これにより、Lambda 関数は、カスタマイズされたデータとレスポンスヘッダーを GetObject 発信者に提供できるようになります。

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 com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.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 { AmazonS3 s3Client = AmazonS3Client.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(new WriteGetObjectResponseRequest() .withRequestRoute(event.outputRoute()) .withRequestToken(event.outputToken()) .withStatusCode(403) .withContentLength(0L).withInputStream(new ByteArrayInputStream(new byte[0])) .withErrorCode("MissingRequiredToken") .withErrorMessage("The required token was not present in the request.")); 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(new WriteGetObjectResponseRequest() .withRequestRoute(event.outputRoute()) .withRequestToken(event.outputToken()) .withInputStream(presignedResponse.body())); } }
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 リクエストは、オブジェクト全体を 1 回の呼び出しでリクエスト元のアプリケーションに返します。

Java

package com.amazon.s3.objectlambda; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.WriteGetObjectResponseRequest; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.awt.Image; import java.io.ByteArrayInputStream; 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 Example2 { private static final int HEIGHT = 250; private static final int WIDTH = 250; public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception { AmazonS3 s3Client = AmazonS3Client.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(new WriteGetObjectResponseRequest() .withRequestRoute(event.outputRoute()) .withRequestToken(event.outputToken()) .withInputStream(new ByteArrayInputStream(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 com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.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 { AmazonS3 s3Client = AmazonS3Client.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(new WriteGetObjectResponseRequest() .withRequestRoute(event.outputRoute()) .withRequestToken(event.outputToken()) .withInputStream(bodyStream)); } }
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 では、発信者に完全なレスポンスを送信するのに最大 60 秒かかります。WriteGetObjectResponse リクエストの場合、実際の利用可能な時間は短くなる可能性があります。例えば、Lambda 関数のタイムアウトが 60 秒未満である可能性があります。それ以外にも、発信者のタイムアウトがより厳しい場合があります。

元の発信者が HTTP ステータスコード 500 以外のレスポンス (内部サーバエラー) を受信するには、WriteGetObjectResponse コールが完了する必要があります。WriteGetObjectResponse API オペレーションが呼び出される前に、Lambda 関数が例外的に、またはその他の方法で返された場合、元の発信者は 500 (内部サーバーエラー) レスポンスを受け取ります。レスポンスが完了するまでの間にスローされた例外は、発信者へのレスポンスが切り捨てられます。Lambda 関数が WriteGetObjectResponse API コールから HTTP ステータスコード 200 (OK) レスポンスを受信した場合、元の発信者は完全なリクエストを送信しています。例外がスローされたかどうかにかかわらず、Lambda 関数のレスポンスは S3 Object Lambda では無視されます。

この WriteGetObjectResponse API オペレーションを呼び出すとき、Amazon S3 はイベントコンテキストからのルートトークンとリクエストトークンを必要とします。詳細については、「イベントコンテキストの形式と使用法」を参照してください。

ルートトークンとリクエストトークンのパラメータは、WriteGetObjectResult レスポンスを元の発信者に接続するために必要です。通常、500 (内部サーバーエラー) レスポンスを再試行するのが適切ですが、リクエストトークンはワンタイムトークンであることに注意してください。それ以降に使用を試みると、HTTP ステータスコード 400 (Bad Request) レスポンスが返されることになる可能性があります。ルートトークンとリクエストトークンを使用した WriteGetObjectResponse への呼び出しは、呼び出された Lambda 関数から作成する必要はありませんが、同じアカウント内のアイデンティティによって作成する必要があります。この呼び出しは、Lambda 関数の実行を完了する前に完了する必要があります。

Lambda での HeadObject リクエストの使用

このセクションでは、Object Lambda アクセスポイントが HeadObject 用に Lambda 関数を呼び出すように設定されていることを前提としています。Lambda は headObjectContext というキーを含む JSON ペイロードを受け取ります。コンテキスト内には、inputS3Url というプロパティが 1 つあります。これは、HeadObject をサポートしているアクセスポイントの署名付き URL です。

署名付き URL に次のプロパティが指定されている場合、署名付き URL にはそれらが含まれます。

  • versionId (クエリパラメータ内)

  • requestPayer (x-amz-request-payer ヘッダー内)

  • expectedBucketOwner (x-amz-expected-bucket-owner ヘッダー内)

他のプロパティは署名付きではないため、含まれません。ヘッダーとして送信される署名されていないオプションは、userRequest ヘッダーにある署名付き URL を呼び出すときに、手動でリクエストに追加できます。サーバー側の暗号化オプションは、HeadObject ではサポートされていません。

リクエスト構文 URI パラメータについては、「Amazon Simple Storage Service API リファレンス」の「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> ... }; }

次の例は、JSON を返す前に必要に応じてヘッダー値を変更することにより、署名付き URL を使用してレスポンスを入力する方法を示しています。

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 には、inputS3Url という単一のプロパティが含まれており、これは ListObjects をサポートしているアクセスポイント用の署名付き URL です。

GetObjectHeadObject とは異なり、署名付き URL には、次のプロパティが指定されている場合はそれらが含まれます。

  • すべてのクエリパラメータ

  • requestPayer (x-amz-request-payer ヘッダー内)

  • expectedBucketOwner (x-amz-expected-bucket-owner ヘッダー内)

リクエスト構文 URI パラメータについては、「Amazon Simple Storage Service API リファレンス」の「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 には、inputS3Url という単一のプロパティが含まれており、これは ListObjectsV2 をサポートしているアクセスポイント用の署名付き URL です。

GetObjectHeadObject とは異なり、署名付き URL には、次のプロパティが指定されている場合はそれらが含まれます。

  • すべてのクエリパラメータ

  • requestPayer (x-amz-request-payer ヘッダー内)

  • expectedBucketOwner (x-amz-expected-bucket-owner ヘッダー内)

リクエスト構文 URI パラメータについては、「Amazon Simple Storage Service API リファレンス」の「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' }, ... ], } }