

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 叫用 AWS Lambda 函數
<a name="batch-ops-invoke-lambda"></a>

您可以使用 Amazon S3 Batch Operations 對 Amazon S3 物件執行大規模的批次操作。**叫用 AWS Lambda 函數**批次操作操作會 AWS Lambda 啟動函數，以對資訊清單中列出的物件執行自訂動作。本節說明如何建立 Lambda 函數以搭配 S3 批次作業使用，以及如何建立任務來叫用函數。S3 批次操作任務會使用 `LambdaInvoke` 操作，對資訊清單中列出的每個物件執行 Lambda 函數。

您可以使用 Amazon S3 主控台、 AWS Command Line Interface (AWS CLI)、 AWS SDKs 或 Amazon S3 REST API Amazon S3 批次操作。 Amazon S3 如需有關使用 Lambda 的詳細資訊，請參閱*《AWS Lambda 開發人員指南》*中的 [AWS Lambda入門](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html)。

下列各節說明如何開始搭配 Lambda 使用 S3 批次作業。

**Topics**
+ [

## 搭配 Batch Operations 使用 Lambda
](#batch-ops-invoke-lambda-using)
+ [

## 建立 Lambda 函數以搭配 S3 批次作業使用
](#batch-ops-invoke-lambda-custom-functions)
+ [

## 建立 S3 批次作業任務以叫用 Lambda 函數
](#batch-ops-invoke-lambda-create-job)
+ [

## 在 Lambda 資訊清單中提供工作層級的資訊
](#storing-task-level-information-in-lambda)
+ [

## S3 Batch Operations 教學課程
](#batch-ops-tutorials-lambda)

## 搭配 Batch Operations 使用 Lambda
<a name="batch-ops-invoke-lambda-using"></a>

搭配 S3 Batch Operations 使用 時 AWS Lambda，您必須建立專門用於 S3 Batch Operations 的新 Lambda 函數。您無法搭配 S3 批次作業重複使用現有基於 Amazon S3 事件的函數。事件函數只能接收訊息；不能傳回訊息。搭配 S3 批次作業使用的 Lambda 函數必須接受並傳回訊息。如需將 Lambda 與 Amazon S3 事件搭配使用的詳細資訊，請參閱《 *AWS Lambda 開發人員指南*》中的[將 AWS Lambda 與 Amazon S3](https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html) 搭配使用。

您建立 S3 批次作業任務來叫用 Lambda 函數。此任務對資訊清單中列出的所有物件執行相同的 Lambda 函數。您可以控制在處理清單中的物件時，使用 Lambda 函數的哪些版本。S3 批次作業支援不合格的 Amazon Resource Name (ARN)、別名和特定版本。如需詳細資訊，請參閱《 *AWS Lambda 開發人員指南*》中的[AWS Lambda 版本控制簡介](https://docs.aws.amazon.com/lambda/latest/dg/versioning-intro.html)。

如果您為 S3 批次作業任務提供的函數 ARN 使用別名或 `$LATEST` 限定詞，而且您更新其中任一項所指向的版本，則 S3 批次作業會開始呼叫 Lambda 函數的新版本。當您想要隨著大型工作更新部分功能，此功能會很有用。如果您不希望 S3 Batch Operations 變更使用的版本，請在建立作業時在 `FunctionARN` 參數中提供特定版本。

具有 S3 Batch Operations 的單一 AWS Lambda 任務可支援具有高達 200 億個物件的資訊清單。

### 搭配目錄儲存貯體使用 Lambda 和 Batch Operations
<a name="batch-ops-invoke-lambda-directory-buckets"></a>

目錄儲存貯體是一種 Amazon S3 儲存貯體，這是專為需要一致的個位數毫秒延遲的工作負載或效能關鍵應用程式所設計的類型。如需詳細資訊，請參閱[目錄儲存貯體](https://docs.aws.amazon.com//AmazonS3/latest/userguide/directory-buckets-overview.html)。

使用 Batch Operations 調用對目錄儲存貯體執行動作的 Lambda 函式時，須遵循特殊需求。例如，您必須使用更新的 JSON 結構描述來建構 Lambda 請求，並在建立作業時指定 [https://docs.aws.amazon.com//AmazonS3/latest/API/API_control_LambdaInvokeOperation.html#AmazonS3-Type-control_LambdaInvokeOperation-InvocationSchemaVersion](https://docs.aws.amazon.com//AmazonS3/latest/API/API_control_LambdaInvokeOperation.html#AmazonS3-Type-control_LambdaInvokeOperation-InvocationSchemaVersion) 2.0 (而不是 1.0)。此更新的結構描述可讓您為 [https://docs.aws.amazon.com//AmazonS3/latest/API/API_control_LambdaInvokeOperation.html#AmazonS3-Type-control_LambdaInvokeOperation-UserArguments](https://docs.aws.amazon.com//AmazonS3/latest/API/API_control_LambdaInvokeOperation.html#AmazonS3-Type-control_LambdaInvokeOperation-UserArguments) 指定選用的索引鍵值配對，您可以用它來修改現有 Lambda 函式的特定參數。如需詳細資訊，請參閱 AWS 儲存部落格[中的使用 Amazon S3 S3 目錄儲存貯體中的物件處理 AWS Lambda](https://aws.amazon.com/blogs/storage/automate-object-processing-in-amazon-s3-directory-buckets-with-s3-batch-operations-and-aws-lambda/)。

### 回應代碼和結果代碼
<a name="batch-ops-invoke-lambda-response-codes"></a>

S3 Batch Operations 會使用一或多個金鑰調用 Lambda 函數，每個金鑰都有相關聯的 `TaskID`。S3 Batch Operations 預期會從 Lambda 函數收到每個金鑰的結果碼。在請求中傳送的任何任務 ID 若未傳回每個金鑰的結果碼，則會從 `treatMissingKeysAs` 欄位得到結果碼。`treatMissingKeysAs` 是選用的請求欄位，預設為 `TemporaryFailure`。下表包含 `treatMissingKeysAs` 欄位的其他可能結果碼和值。


| 回應代碼 | 描述 | 
| --- | --- | 
| Succeeded | 任務正常完成。如果您請求工作完成報告，即會在報告中包含任務的結果字串。 | 
| TemporaryFailure | 任務遇到暫時性的失敗，並且將在工作完成之前重新推動。會忽略結果字串。如果這是最後一次的重新推動，即會在最終報告中包含錯誤訊息。 | 
| PermanentFailure | 任務遇到永久的失敗。如果您請求工作完成報告，即會將任務標示為 Failed，並包含錯誤訊息字串。會忽略來自失敗任務的結果字串。 | 

## 建立 Lambda 函數以搭配 S3 批次作業使用
<a name="batch-ops-invoke-lambda-custom-functions"></a>

本節提供必須搭配 Lambda 函數使用的範例 AWS Identity and Access Management (IAM) 許可。還包含一個搭配 S3 批次作業使用的 Lambda 函數範例。如果您之前從未建立過 Lambda 函數，請參閱《 *AWS Lambda 開發人員指南*》中的[教學課程： AWS Lambda 搭配 Amazon S3 使用](https://docs.aws.amazon.com/lambda/latest/dg/with-s3-example.html) 。

您必須建立專門搭配 S3 批次作業使用的 Lambda 函數。您無法重複使用現有的 Amazon S3 事件型 Lambda 函數，因為用於 S3 Batch Operations 的 Lambda 函數必須接受並傳回特殊資料欄位。

**重要**  
AWS Lambda 以 Java 編寫的 函數接受 [https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/RequestHandler.java](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/RequestHandler.java)或 [https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/RequestStreamHandler.java](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/RequestStreamHandler.java)處理常式界面。不過，若要支援 S3 Batch Operations 請求和回應格式， AWS Lambda 需要 `RequestStreamHandler`界面，以進行請求和回應的自訂序列化和還原序列化。此介面允許 Lambda 將 InputStream 和 OutputStream 傳遞給 Java `handleRequest` 方法。  
搭配 S3 批次作業使用 Lambda 函數時，請務必使用 `RequestStreamHandler` 介面。如果您使用 `RequestHandler` 介面，批次任務會失敗，完成報告中出現「Lambda 承載中傳回無效的 JSON」。  
如需詳細資訊，請參閱*《AWS Lambda 使用者指南》*中的[處理常式介面](https://docs.aws.amazon.com//lambda/latest/dg/java-handler.html#java-handler-interfaces)。

### IAM 權限範例
<a name="batch-ops-invoke-lambda-custom-functions-iam"></a>

以下是搭配 S3 批次作業使用 Lambda 函數所需的 IAM 權限範例。

**Example — S3 批次作業信任政策**  
以下是可用於批次作業 IAM 角色的信任政策範例。您在建立任務時指定此 IAM 角色，以准許批次作業擔任 IAM 角色。    
****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "batchoperations.s3.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

**Example — Lambda IAM 政策**  
以下 IAM 政策範例准許 S3 批次作業叫用 Lambda 函數和讀取輸入資訊清單。    
****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "BatchOperationsLambdaPolicy",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:PutObject",
                "lambda:InvokeFunction"
            ],
            "Resource": "*"
        }
    ]
}
```

### 範例請求和回應
<a name="batch-ops-invoke-lambda-custom-functions-request"></a>

本節提供 Lambda 函數的請求和回應範例。

**Example 請求**  
以下是 Lambda 函數請求的 JSON 範例。  

```
{
    "invocationSchemaVersion": "1.0",
    "invocationId": "YXNkbGZqYWRmaiBhc2RmdW9hZHNmZGpmaGFzbGtkaGZza2RmaAo",
    "job": {
        "id": "f3cc4f60-61f6-4a2b-8a21-d07600c373ce"
    },
    "tasks": [
        {
            "taskId": "dGFza2lkZ29lc2hlcmUK",
            "s3Key": "customerImage1.jpg",
            "s3VersionId": "1",
            "s3BucketArn": "arn:aws:s3:us-east-1:0123456788:amzn-s3-demo-bucket1"
        }
    ]
}
```

**Example 回應**  
以下是 Lambda 函數回應的 JSON 範例。  

```
{
  "invocationSchemaVersion": "1.0",
  "treatMissingKeysAs" : "PermanentFailure",
  "invocationId" : "YXNkbGZqYWRmaiBhc2RmdW9hZHNmZGpmaGFzbGtkaGZza2RmaAo",
  "results": [
    {
      "taskId": "dGFza2lkZ29lc2hlcmUK",
      "resultCode": "Succeeded",
      "resultString": "[\"Mary Major", \"John Stiles\"]"
    }
  ]
}
```

### S3 批次作業的 Lambda 函數範例
<a name="batch-ops-invoke-lambda-custom-functions-example"></a>

以下範例 Python Lambda 從版本控制的物件中移除了刪除標記。

如範例所示，來自 S3 批次作業的金鑰以 URL 編碼。若要將 Amazon S3 與其他 AWS 服務搭配使用，請務必對從 S3 Batch Operations 傳遞的金鑰進行 URL 解碼。

```
import logging
from urllib import parse
import boto3
from botocore.exceptions import ClientError

logger = logging.getLogger(__name__)
logger.setLevel("INFO")

s3 = boto3.client("s3")


def lambda_handler(event, context):
    """
    Removes a delete marker from the specified versioned object.

    :param event: The S3 batch event that contains the ID of the delete marker
                  to remove.
    :param context: Context about the event.
    :return: A result structure that Amazon S3 uses to interpret the result of the
             operation. When the result code is TemporaryFailure, S3 retries the
             operation.
    """
    # Parse job parameters from Amazon S3 batch operations
    invocation_id = event["invocationId"]
    invocation_schema_version = event["invocationSchemaVersion"]

    results = []
    result_code = None
    result_string = None

    task = event["tasks"][0]
    task_id = task["taskId"]

    try:
        obj_key = parse.unquote_plus(task["s3Key"], encoding="utf-8")
        obj_version_id = task["s3VersionId"]
        bucket_name = task["s3BucketArn"].split(":")[-1]

        logger.info(
            "Got task: remove delete marker %s from object %s.", obj_version_id, obj_key
        )

        try:
            # If this call does not raise an error, the object version is not a delete
            # marker and should not be deleted.
            response = s3.head_object(
                Bucket=bucket_name, Key=obj_key, VersionId=obj_version_id
            )
            result_code = "PermanentFailure"
            result_string = (
                f"Object {obj_key}, ID {obj_version_id} is not " f"a delete marker."
            )

            logger.debug(response)
            logger.warning(result_string)
        except ClientError as error:
            delete_marker = error.response["ResponseMetadata"]["HTTPHeaders"].get(
                "x-amz-delete-marker", "false"
            )
            if delete_marker == "true":
                logger.info(
                    "Object %s, version %s is a delete marker.", obj_key, obj_version_id
                )
                try:
                    s3.delete_object(
                        Bucket=bucket_name, Key=obj_key, VersionId=obj_version_id
                    )
                    result_code = "Succeeded"
                    result_string = (
                        f"Successfully removed delete marker "
                        f"{obj_version_id} from object {obj_key}."
                    )
                    logger.info(result_string)
                except ClientError as error:
                    # Mark request timeout as a temporary failure so it will be retried.
                    if error.response["Error"]["Code"] == "RequestTimeout":
                        result_code = "TemporaryFailure"
                        result_string = (
                            f"Attempt to remove delete marker from  "
                            f"object {obj_key} timed out."
                        )
                        logger.info(result_string)
                    else:
                        raise
            else:
                raise ValueError(
                    f"The x-amz-delete-marker header is either not "
                    f"present or is not 'true'."
                )
    except Exception as error:
        # Mark all other exceptions as permanent failures.
        result_code = "PermanentFailure"
        result_string = str(error)
        logger.exception(error)
    finally:
        results.append(
            {
                "taskId": task_id,
                "resultCode": result_code,
                "resultString": result_string,
            }
        )
    return {
        "invocationSchemaVersion": invocation_schema_version,
        "treatMissingKeysAs": "PermanentFailure",
        "invocationId": invocation_id,
        "results": results,
    }
```

## 建立 S3 批次作業任務以叫用 Lambda 函數
<a name="batch-ops-invoke-lambda-create-job"></a>

建立 S3 批次作業任務來叫用 Lambda 函數時，您必須提供下列項目：
+ Lambda 函數的 ARN (可能包含函數別名或特定版本號碼)
+ 獲准叫用函數的 IAM 角色
+ 動作參數 `LambdaInvokeFunction`

如需有關建立 S3 批次作業任務的詳細資訊，請參閱[建立 S3 批次操作任務](batch-ops-create-job.md)和[S3 批次操作支援的操作](batch-ops-operations.md)。

下列範例會建立一個 S3 Batch Operations 作業，使用 AWS CLI來調用 Lambda 函數。若要使用此範例，請以您自己的資訊取代 *`user input placeholders`*。

```
aws s3control create-job
    --account-id account-id
    --operation  '{"LambdaInvoke": { "FunctionArn": "arn:aws:lambda:region:account-id:function:LambdaFunctionName" } }'
    --manifest '{"Spec":{"Format":"S3BatchOperations_CSV_20180820","Fields":["Bucket","Key"]},"Location":{"ObjectArn":"arn:aws:s3:::amzn-s3-demo-manifest-bucket","ETag":"ManifestETag"}}'
    --report '{"Bucket":"arn:aws:s3:::amzn-s3-demo-bucket","Format":"Report_CSV_20180820","Enabled":true,"Prefix":"ReportPrefix","ReportScope":"AllTasks"}'
    --priority 2
    --role-arn arn:aws:iam::account-id:role/BatchOperationsRole
    --region region
    --description "Lambda Function"
```

## 在 Lambda 資訊清單中提供工作層級的資訊
<a name="storing-task-level-information-in-lambda"></a>

當您搭配 S3 Batch Operations 使用 AWS Lambda 函數時，您可能會希望每個操作的任務或金鑰隨附額外的資料。例如，您可能希望同時提供來源物件金鑰與全新的物件金鑰。這樣 Lambda 函數就能以新名稱，將來源金鑰複製到新的 S3 儲存貯體。根據預設，Batch Operations 可讓您在作業的輸入資訊清單中僅指定目的地儲存貯體和來源金鑰清單。下列範例說明如何在資訊清單中包含其他資料，以便執行更複雜的 Lambda 函數。

若要在 S3 批次作業資訊清單中指定每個金鑰的參數，以用於 Lambda 函數程式碼中，請使用以下 URL 編碼的 JSON 格式。`key` 欄位視同 Amazon S3 物件金鑰傳遞給 Lambda 函數。但 Lambda 函數可以轉譯此欄位，以包含其他值或多個金鑰，如下列範例所示。

**注意**  
資訊清單中 `key` 欄位的字元數上限是 1,024。

**Example — 以 JSON 字串取代「Amazon S3 金鑰」的資訊清單**  
必須提供以 URL 編碼的版本給 S3 批次作業。  

```
amzn-s3-demo-bucket,{"origKey": "object1key", "newKey": "newObject1Key"}
amzn-s3-demo-bucket,{"origKey": "object2key", "newKey": "newObject2Key"}
amzn-s3-demo-bucket,{"origKey": "object3key", "newKey": "newObject3Key"}
```

**Example — 以 URL 編碼的資訊清單**  
必須提供這個以 URL 編碼的版本給 S3 批次作業。非 URL 編碼的版本不會運作。  

```
amzn-s3-demo-bucket,%7B%22origKey%22%3A%20%22object1key%22%2C%20%22newKey%22%3A%20%22newObject1Key%22%7D
amzn-s3-demo-bucket,%7B%22origKey%22%3A%20%22object2key%22%2C%20%22newKey%22%3A%20%22newObject2Key%22%7D
amzn-s3-demo-bucket,%7B%22origKey%22%3A%20%22object3key%22%2C%20%22newKey%22%3A%20%22newObject3Key%22%7D
```

**Example — 以資訊清單格式將結果寫入任務報告的 Lambda 函數**  
此 URL 編碼的資訊清單範例包含縱線分隔的物件金鑰，供下列 Lambda 函數剖析。  

```
amzn-s3-demo-bucket,object1key%7Clower
amzn-s3-demo-bucket,object2key%7Cupper
amzn-s3-demo-bucket,object3key%7Creverse
amzn-s3-demo-bucket,object4key%7Cdelete
```
此 Lambda 函數示範如何剖析已編碼成 S3 Batch Operations 資訊清單的縱線分隔任務。任務會指出要套用至指定物件的修訂版作業。  

```
import logging
from urllib import parse
import boto3
from botocore.exceptions import ClientError

logger = logging.getLogger(__name__)
logger.setLevel("INFO")

s3 = boto3.resource("s3")


def lambda_handler(event, context):
    """
    Applies the specified revision to the specified object.

    :param event: The Amazon S3 batch event that contains the ID of the object to
                  revise and the revision type to apply.
    :param context: Context about the event.
    :return: A result structure that Amazon S3 uses to interpret the result of the
             operation.
    """
    # Parse job parameters from Amazon S3 batch operations
    invocation_id = event["invocationId"]
    invocation_schema_version = event["invocationSchemaVersion"]

    results = []
    result_code = None
    result_string = None

    task = event["tasks"][0]
    task_id = task["taskId"]
    # The revision type is packed with the object key as a pipe-delimited string.
    obj_key, revision = parse.unquote_plus(task["s3Key"], encoding="utf-8").split("|")
    bucket_name = task["s3BucketArn"].split(":")[-1]

    logger.info("Got task: apply revision %s to %s.", revision, obj_key)

    try:
        stanza_obj = s3.Bucket(bucket_name).Object(obj_key)
        stanza = stanza_obj.get()["Body"].read().decode("utf-8")
        if revision == "lower":
            stanza = stanza.lower()
        elif revision == "upper":
            stanza = stanza.upper()
        elif revision == "reverse":
            stanza = stanza[::-1]
        elif revision == "delete":
            pass
        else:
            raise TypeError(f"Can't handle revision type '{revision}'.")

        if revision == "delete":
            stanza_obj.delete()
            result_string = f"Deleted stanza {stanza_obj.key}."
        else:
            stanza_obj.put(Body=bytes(stanza, "utf-8"))
            result_string = (
                f"Applied revision type '{revision}' to " f"stanza {stanza_obj.key}."
            )

        logger.info(result_string)
        result_code = "Succeeded"
    except ClientError as error:
        if error.response["Error"]["Code"] == "NoSuchKey":
            result_code = "Succeeded"
            result_string = (
                f"Stanza {obj_key} not found, assuming it was deleted "
                f"in an earlier revision."
            )
            logger.info(result_string)
        else:
            result_code = "PermanentFailure"
            result_string = (
                f"Got exception when applying revision type '{revision}' "
                f"to {obj_key}: {error}."
            )
            logger.exception(result_string)
    finally:
        results.append(
            {
                "taskId": task_id,
                "resultCode": result_code,
                "resultString": result_string,
            }
        )
    return {
        "invocationSchemaVersion": invocation_schema_version,
        "treatMissingKeysAs": "PermanentFailure",
        "invocationId": invocation_id,
        "results": results,
    }
```

## S3 Batch Operations 教學課程
<a name="batch-ops-tutorials-lambda"></a>

下列教學課程會針對部分搭配 Lambda 的批次操作工作，提供完整的端對端程序。在本教學課程中，您將了解如何設定 Batch Operations 來調用 Lambda 函數，以對儲存在 S3 來源儲存貯體中的影片進行批次轉碼。Lambda 函數會呼叫 AWS Elemental MediaConvert 來轉碼影片。
+ [教學課程：使用 S3 Batch Operations 進行影片的批次轉碼](tutorial-s3-batchops-lambda-mediaconvert-video.md)