本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
撰寫 S3 Object Lambda 存取點的 Lambda 函數
本節詳細說明如何撰寫與 Amazon S3 物件 Lambda 存取點搭配使用的 AWS Lambda 函數。
若要了解某些 S3 物件 Lambda 任務的完整 end-to-end 程序,請參閱下列內容:
使用 Lambda 中的 GetObject
請求
本節假設您的 Object Lambda 存取點已設定為呼叫 GetObject
的 Lambda 函數。S3 物件 Lambda 包含 Amazon S3 API 作業WriteGetObjectResponse
,可讓 Lambda 函數向GetObject
呼叫者提供自訂的資料和回應標頭。
WriteGetObjectResponse
可讓您根據處理需求,全面控制狀態碼、回應標頭和回應本文。您可以使用 WriteGetObjectResponse
,以整個轉換的物件、轉換物件的部分,或是基於應用程式內容的其他回應進行回應。下節展示使用此WriteGetObjectResponse
API作業的唯一範例。
範例 1:使用HTTP狀態碼 403 回應 (禁止)
您可WriteGetObjectResponse
以使用根據對象的內容響應HTTP狀態碼 403(禁止)。
- 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
請求會在一次呼叫中,將整個物件傳回給請求的應用程式。
- 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
叫必須完成。如果 Lambda 函數在呼叫WriteGetObjectResponse
API作業之前傳回 (發生例外或其他情況),則原始呼叫者會收到 500 (內部伺服器錯誤) 回應。在完成回應所需的時間內擲出的異常會導致對呼叫者的回應截斷。如果 Lambda 函數收到來自呼叫的HTTP狀態碼 200 (OK) 回應,則原始呼叫者已傳送完整的要求。WriteGetObjectResponse
API無論是否擲出異常,S3 Object Lambda 都會忽略 Lambda 函數的回應。
呼叫WriteGetObjectResponse
API作業時,Amazon S3 需要來自事件內容的路由和請求權杖。如需詳細資訊,請參閱事件內容格式和用量。
路由和請求字符參數是必要項目,用來將 WriteGetObjectResult
回應與原始呼叫者連線。即使重試 500(內部服務器錯誤)響應始終是合適的,因為請求令牌是單次使用令牌,後續嘗試使用它可能會導致HTTP狀態碼 400(錯誤請求)響應。雖然使用路由和請求字符的 WriteGetObjectResponse
呼叫不需要從叫用的 Lambda 函數建立,但必須由同一帳戶中的身分進行呼叫。在 Lambda 函數完成執行之前,還必須完成呼叫。
使用 Lambda 中的 HeadObject
請求
本節假設您的 Object Lambda 存取點已設定為呼叫 HeadObject
的 Lambda 函數。Lambda 將收到一個包含名為的密鑰的JSON有效載荷headObjectContext
。在內容中,有一個名為的屬性inputS3Url
,這是支援存取點URL的預先簽署。HeadObject
如果已指定,預先簽署URL將包含下列屬性:
其他屬性不會預先簽章,因此不會包含在內。呼叫標頭中的預先簽署時,可以手動將以標頭形式傳送的非簽署URL選項新增至要求。userRequest
HeadObject
不支援伺服器端加密選項。
有關請求語法URI參數,請參閱 Amazon 簡單儲存服務參API考HeadObject
中的。
下列範例顯示的 Lambda JSON 輸入有效負載HeadObject
。
{
"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 函數應該返回一個JSON對象,其中包含將為HeadObject
調用返回的標題和值。
下列範例顯示的 Lambda 回應JSON結構HeadObject
。
{
"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>
...
};
}
下列範例會示範如何使用 presigned 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 將接收帶有名為的新對象的有JSON效負載listObjectsContext
。 listObjectsContext
包含單一屬性inputS3Url
,此屬性URL為的支援存取點預先簽署。ListObjects
與 GetObject
and 不同HeadObject
,如果指定了以下屬性,則預先簽署URL將包含下列屬性:
有關請求語法URI參數,請參閱 Amazon 簡單儲存服務參API考ListObjects
中的。
我們建議您在開發應用程式時使用較新版本 ListObjectsV2。為了回溯相容性,Amazon S3 繼續支援 ListObjects
。
下列範例顯示的 Lambda JSON 輸入承載資料ListObjects
。
{
"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 JSON 物件 Lambda 傳回的狀態碼、清單XML結果或錯誤資訊的物件。
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
下列範例顯示的 Lambda 回應JSON結構ListObjects
。
{
"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 將接收帶有名為的新對象的有JSON效負載listObjectsV2Context
。 listObjectsV2Context
包含單一屬性inputS3Url
,此屬性URL為的支援存取點預先簽署。ListObjectsV2
與 GetObject
and 不同HeadObject
,如果指定了以下屬性,則預先簽署URL將包含下列屬性:
有關請求語法URI參數,請參閱 Amazon 簡單儲存服務參API考ListObjectsV2
中的。
下列範例顯示的 Lambda JSON 輸入承載資料ListObjectsV2
。
{
"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 JSON 物件 Lambda 傳回的狀態碼、清單XML結果或錯誤資訊的物件。
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
下列範例顯示的 Lambda 回應JSON結構ListObjectsV2
。
{
"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'
},
...
],
}
}