

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

# 使用 Amazon Rekognition 和 Lambda 在 Amazon S3 儲存貯體中標記資產
<a name="images-lambda-s3-tutorial"></a>

在本教學課程中，您會建立 AWS Lambda函數，自動標記位於 Amazon S3 儲存貯體中的數位資產。Lambda 函數可讀取指定 Amazon S3 儲存貯體中的所有物件。對於儲存貯體中的每個物件，它會將映像傳遞至 Amazon Rekognition 服務，以建立一系列標籤。每個標籤都用來建立套用至影像的標籤。執行 Lambda 函數後，其會根據指定 Amazon S3 儲存貯體中的所有映像自動建立標籤，並將其套用至映像檔。

例如，假設您執行 Lambda 函數，而 Amazon S3 儲存貯體中有這個映像。

![Volcano 在混濁的天空上，以融化的灌木叢向下串流。](http://docs.aws.amazon.com/zh_tw/rekognition/latest/dg/images/v2-image-tutorial-picture.png)


然後，應用程式會自動建立標籤並將其套用至影像。

![表格顯示追蹤儲存成本的標籤，包括具有數值的Nature、Volcano、Eruption、Lava、Mountain 和Outdoors。](http://docs.aws.amazon.com/zh_tw/rekognition/latest/dg/images/v2-image-tutorial-results.png)


**注意**  
您在本教學課程中使用的服務是 AWS免費方案的一部分。完成教學課程後，我們建議您終止在教學課程中建立的任何資源，這樣就不會被收取任何費用。

本教學課程使用適用於 Java 的 AWSSDK 第 2 版。如需其他 Java V2 教學課程，請參閱 [AWS 文件 SDK 範例 GitHub 儲存庫](https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javav2/usecases)。

**Topics**
+ [先決條件](#lambda-s3-tutorial-prerequisites)
+ [設定 IAM Lambda 角色](#lambda-s3-tutorial-lambda-role)
+ [建立專案](#lambda-s3-tutorial-pom)
+ [撰寫程式碼](#lambda-s3-tutorial-code)
+ [Package 專案](#lambda-s3-tutorial-package)
+ [部署 Lambda 函數。](#lambda-s3-tutorial-deploy)
+ [測試 Lambda 方法](#lambda-s3-tutorial-test)

## 先決條件
<a name="lambda-s3-tutorial-prerequisites"></a>

開始之前，您需要完成[設定適用於 Java 的 AWSSDK](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html) 中的步驟。請確定執行下列操作：
+ Java 1.8 JDK.
+ Maven 3.6 或更高版本。
+ 一個 [Amazon S3 儲存貯體](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html)，其中包含 5 至 7 個自然映像。這些映像由 Lambda 函數讀取。

## 設定 IAM Lambda 角色
<a name="lambda-s3-tutorial-lambda-role"></a>

本教學使用 Amazon Rekognition 和 Amazon S3 服務。將 ** Lambda 支援 ** 角色設定為具有可讓其從 Lambda 函數調用這些服務的政策。

**設定角色**

1. 登入 AWS 管理主控台，並在 https：//[https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/) 開啟 IAM 主控台。

1. 在導覽窗格中，選擇**角色**，然後選擇**建立角色**。

1. 選擇**AWS服務**，接著選擇**Lambda**。

1. 選擇**許可**標籤。

1. 搜尋 **AWSLambdaBasicExecutionRole**。

1. 選擇**下一個標籤**。

1. 選擇**檢閱**。

1. 對角色 **Lambda 支援**命名。

1. 選擇建**立角色**。

1. 選擇 **Lambda 支援**來檢視概觀頁面。

1. 選擇**連接政策**。

1. 從政策清單中選擇 *AmazonRekognitionFullAccess*。

1. 選擇 **Attach policy** (連接政策)。

1. 搜尋 **AmazonS3FullAccess**，然後選擇**連接政策**。

## 建立專案
<a name="lambda-s3-tutorial-pom"></a>

建立一個新的 Java 專案，然後使用所需的設定和相依性設定 Maven pom.xml。請確定您的 pom.xml 檔案如下所示：

```
<?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>WorkflowTagAssets</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>java-basic-function</name>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.10.54</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
   <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-core</artifactId>
        <version>1.2.1</version>
    </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.8.6</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.10.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.13.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j18-impl</artifactId>
        <version>2.13.3</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.6.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.6.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.googlecode.json-simple</groupId>
        <artifactId>json-simple</artifactId>
        <version>1.1.1</version>
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3</artifactId>
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>rekognition</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.2</version>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.2</version>
            <configuration>
                <createDependencyReducedPom>false</createDependencyReducedPom>
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
  </build>
 </project>
```

## 撰寫程式碼
<a name="lambda-s3-tutorial-code"></a>

使用AWS Lambda執行期 Java API 來建立定義 Lambda 函數的 Java 類別。在此範例中，有一個名為**處理常式**的 Lambda 函數的 Java 類別，以及此使用案例所需的其他類別。下圖顯示專案中的 Java 類別。請注意，所有的 Java 類別都位於一個名為**com.example.tags**的套件中。

![專案結構顯示工作流程標記資產的 Java 類別，例如 AnalyzePhotos、BucketItem、處理常式、S3Service 和 WorkItem。](http://docs.aws.amazon.com/zh_tw/rekognition/latest/dg/images/v2-image-tutorial-files.png)


為程式碼建立下列 Java 類別：
+ **Handler** 使用 Lambda Java 執行時間 API，並執行本AWS教學課程中所述的使用案例。執行的應用程式邏輯位於 handleRequest 方法中。
+ **S3Service** 使用 Amazon S3 API 來執行 S3 操作。
+ **AnalyzePhotos** 使用 Amazon Rekognition API 來分析圖片。
+ **BucketItem** 定義了一個存放 Amazon S3 儲存貯體資訊的模型。
+ **WorkItem**定義了一個存放 Amazon Rekognition 資料的模型。

### 處理常式類別：
<a name="w2aac52b9c25c11"></a>

這個 Java 程式碼代表**處理常式**類別。此類別會讀取傳遞至 Lambda 函數的旗標。**s3Service.ListBucketObjects** 方法會傳回一個 **清單** 物件，其中每個項目都是代表物件索引鍵的字串值。如果旗標值為真，則會透過反覆執行清單並呼叫 **s3Service.tagAssets** 方法，將標籤套用至每個物件，以套用標籤。如果旗標值為假，則會調用刪除標籤的 ** s3Service.deleteTagFromObject ** 方法。此外，請注意，您可以使用 ** LambdaLogger ** 物件，將訊息記錄到 Amazon CloudWatch 日誌。

**注意**  
請務必將儲存貯體名稱指派給 **bucketName** 變數。

```
package com.example.tags;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Handler implements RequestHandler<Map<String,String>, String> {

@Override
public String handleRequest(Map<String, String> event, Context context) {
    LambdaLogger logger = context.getLogger();
    String delFlag = event.get("flag");
    logger.log("FLAG IS: " + delFlag);
    S3Service s3Service = new S3Service();
    AnalyzePhotos photos = new AnalyzePhotos();

    String bucketName = "<Enter your bucket name>";
    List<String> myKeys = s3Service.listBucketObjects(bucketName);
    if (delFlag.compareTo("true") == 0) {

        // Create a List to store the data.
        List<ArrayList<WorkItem>> myList = new ArrayList<>();

        // loop through each element in the List and tag the assets.
        for (String key : myKeys) {

            byte[] keyData = s3Service.getObjectBytes(bucketName, key);

            // Analyze the photo and return a list where each element is a WorkItem.
            ArrayList<WorkItem> item = photos.detectLabels(keyData, key);
            myList.add(item);
        }

        s3Service.tagAssets(myList, bucketName);
        logger.log("All Assets in the bucket are tagged!");

    } else {

        // Delete all object tags.
        for (String key : myKeys) {
            s3Service.deleteTagFromObject(bucketName, key);
            logger.log("All Assets in the bucket are deleted!");
        }
     }
    return delFlag;
  }
 }
```

### S3Service 類
<a name="w2aac52b9c25c13"></a>

下列類別使用 Amazon S3 API 來執行 S3 操作。例如，**getObjectBytes** 方法會傳回代表影像的位元組陣列。同樣地，**listBucketObjects** 方法會傳回**清單**物件，其中每個項目都是指定索引鍵名稱的字串值。

```
 package com.example.tags;

 import software.amazon.awssdk.core.ResponseBytes;
 import software.amazon.awssdk.regions.Region;
 import software.amazon.awssdk.services.s3.S3Client;
 import software.amazon.awssdk.services.s3.model.GetObjectRequest;
 import software.amazon.awssdk.services.s3.model.PutObjectTaggingRequest;
 import software.amazon.awssdk.services.s3.model.GetObjectResponse;
 import software.amazon.awssdk.services.s3.model.S3Exception;
 import software.amazon.awssdk.services.s3.model.ListObjectsResponse;
 import software.amazon.awssdk.services.s3.model.S3Object;
 import software.amazon.awssdk.services.s3.model.GetObjectTaggingResponse;
 import software.amazon.awssdk.services.s3.model.ListObjectsRequest;
 import java.util.ArrayList;
 import java.util.List;
 import software.amazon.awssdk.services.s3.model.Tagging;
 import software.amazon.awssdk.services.s3.model.Tag;
 import software.amazon.awssdk.services.s3.model.GetObjectTaggingRequest;
 import software.amazon.awssdk.services.s3.model.DeleteObjectTaggingRequest;

 public class S3Service {

 private S3Client getClient() {

    Region region = Region.US_WEST_2;
    return S3Client.builder()
            .region(region)
            .build();
 }

 public byte[] getObjectBytes(String bucketName, String keyName) {

    S3Client s3 = getClient();

    try {

        GetObjectRequest objectRequest = GetObjectRequest
                .builder()
                .key(keyName)
                .bucket(bucketName)
                .build();

        // Return the byte[] from this object.
        ResponseBytes<GetObjectResponse> objectBytes = s3.getObjectAsBytes(objectRequest);
        return objectBytes.asByteArray();

    } catch (S3Exception e) {
        System.err.println(e.awsErrorDetails().errorMessage());
        System.exit(1);
    }
    return null;
 }

 // Returns the names of all images in the given bucket.
 public List<String> listBucketObjects(String bucketName) {

    S3Client s3 = getClient();
    String keyName;

    List<String> keys = new ArrayList<>();

    try {
        ListObjectsRequest listObjects = ListObjectsRequest
                .builder()
                .bucket(bucketName)
                .build();

        ListObjectsResponse res = s3.listObjects(listObjects);
        List<S3Object> objects = res.contents();

        for (S3Object myValue: objects) {
            keyName = myValue.key();
            keys.add(keyName);
        }
        return keys;

    } catch (S3Exception e) {
        System.err.println(e.awsErrorDetails().errorMessage());
        System.exit(1);
    }
    return null;
 }

 // Tag assets with labels in the given list.
 public void tagAssets(List myList, String bucketName) {

    try {

        S3Client s3 = getClient();
        int len = myList.size();

        String assetName = "";
        String labelName = "";
        String labelValue = "";

        // Tag all the assets in the list.
        for (Object o : myList) {

            // Need to get the WorkItem from each list.
            List innerList = (List) o;
            for (Object value : innerList) {

                WorkItem workItem = (WorkItem) value;
                assetName = workItem.getKey();
                labelName = workItem.getName();
                labelValue = workItem.getConfidence();
                tagExistingObject(s3, bucketName, assetName, labelName, labelValue);
            }
        }

    } catch (S3Exception e) {
        System.err.println(e.awsErrorDetails().errorMessage());
        System.exit(1);
    }
 }

 // This method tags an existing object.
 private void tagExistingObject(S3Client s3, String bucketName, String key, String label, String LabelValue) {

    try {

        // First need to get existing tag set; otherwise the existing tags are overwritten.
        GetObjectTaggingRequest getObjectTaggingRequest = GetObjectTaggingRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();

        GetObjectTaggingResponse response = s3.getObjectTagging(getObjectTaggingRequest);

        // Get the existing immutable list - cannot modify this list.
        List<Tag> existingList = response.tagSet();
        ArrayList<Tag> newTagList = new ArrayList(new ArrayList<>(existingList));

        // Create a new tag.
        Tag myTag = Tag.builder()
                .key(label)
                .value(LabelValue)
                .build();

        // push new tag to list.
        newTagList.add(myTag);
        Tagging tagging = Tagging.builder()
                .tagSet(newTagList)
                .build();

        PutObjectTaggingRequest taggingRequest = PutObjectTaggingRequest.builder()
                .key(key)
                .bucket(bucketName)
                .tagging(tagging)
                .build();

        s3.putObjectTagging(taggingRequest);
        System.out.println(key + " was tagged with " + label);

    } catch (S3Exception e) {
        System.err.println(e.awsErrorDetails().errorMessage());
        System.exit(1);
    }
  }

 // Delete tags from the given object.
 public void deleteTagFromObject(String bucketName, String key) {

    try {

        DeleteObjectTaggingRequest deleteObjectTaggingRequest = DeleteObjectTaggingRequest.builder()
                .key(key)
                .bucket(bucketName)
                .build();

        S3Client s3 = getClient();
        s3.deleteObjectTagging(deleteObjectTaggingRequest);

    } catch (S3Exception e) {
        System.err.println(e.awsErrorDetails().errorMessage());
        System.exit(1);
    }
  }
}
```

### AnalyzePhoto 類
<a name="w2aac52b9c25c15"></a>

下面的 Java 程式碼表示 **AnalyzePhotos** 類。這個類別使用 Amazon Rekognition API 來分析映像。

```
package com.example.tags;

import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.rekognition.RekognitionClient;
import software.amazon.awssdk.services.rekognition.model.Image;
import software.amazon.awssdk.services.rekognition.model.DetectLabelsRequest;
import software.amazon.awssdk.services.rekognition.model.DetectLabelsResponse;
import software.amazon.awssdk.services.rekognition.model.Label;
import software.amazon.awssdk.services.rekognition.model.RekognitionException;
import java.util.ArrayList;
import java.util.List;

public class AnalyzePhotos {

 // Returns a list of WorkItem objects that contains labels.
 public ArrayList<WorkItem> detectLabels(byte[] bytes, String key) {

    Region region = Region.US_EAST_2;
    RekognitionClient rekClient = RekognitionClient.builder()
            .credentialsProvider(EnvironmentVariableCredentialsProvider.create())
            .region(region)
            .build();

    try {

        SdkBytes sourceBytes = SdkBytes.fromByteArray(bytes);

        // Create an Image object for the source image.
        Image souImage = Image.builder()
                .bytes(sourceBytes)
                .build();

        DetectLabelsRequest detectLabelsRequest = DetectLabelsRequest.builder()
                .image(souImage)
                .maxLabels(10)
                .build();

        DetectLabelsResponse labelsResponse = rekClient.detectLabels(detectLabelsRequest);

        // Write the results to a WorkItem instance.
        List<Label> labels = labelsResponse.labels();
        ArrayList<WorkItem> list = new ArrayList<>();
        WorkItem item ;
        for (Label label: labels) {
            item = new WorkItem();
            item.setKey(key); // identifies the photo.
            item.setConfidence(label.confidence().toString());
            item.setName(label.name());
            list.add(item);
        }
        return list;

    } catch (RekognitionException e) {
        System.out.println(e.getMessage());
        System.exit(1);
    }
    return null ;
  }
}
```

### BucketItem 類
<a name="w2aac52b9c25c17"></a>

下列 Java 程式碼代表儲存 Amazon S3 儲存貯體 **BucketItem** 類。

```
package com.example.tags;

public class BucketItem {

 private String key;
 private String owner;
 private String date ;
 private String size ;


 public void setSize(String size) {
    this.size = size ;
 }

 public String getSize() {
    return this.size ;
 }

 public void setDate(String date) {
    this.date = date ;
 }

 public String getDate() {
    return this.date ;
 }

 public void setOwner(String owner) {
    this.owner = owner ;
 }

 public String getOwner() {
    return this.owner ;
 }

 public void setKey(String key) {
    this.key = key ;
 }

 public String getKey() {
    return this.key ;
 }
}
```

### WorkItem 類
<a name="w2aac52b9c25c19"></a>

下列 Java 程式碼代表 **WorkItem** 類。

```
 package com.example.tags;

 public class WorkItem {

 private String key;
 private String name;
 private String confidence ;

public void setKey (String key) {
    this.key = key;
}

public String getKey() {
    return this.key;
}

public void setName (String name) {
    this.name = name;
}

public String getName() {
    return this.name;
}

public void setConfidence (String confidence) {
    this.confidence = confidence;
}

public String getConfidence() {
    return this.confidence;
}

}
```

## Package 專案
<a name="lambda-s3-tutorial-package"></a>

透過使用下面的 Maven 命令將該專案打包到一個 .jar (JAR) 檔案。

```
mvn package
```

JAR 檔案位於**目標**資料夾 (專案資料夾的子資料夾) 中。

![檔案總管視窗顯示具有 WorkflowTagAssets-1.0-SNAPSHOT.jar 等 JAR 檔案的目標資料夾，以及其他專案檔案和資料夾。](http://docs.aws.amazon.com/zh_tw/rekognition/latest/dg/images/v2-image-tutorial-folder.png)


**注意**  
請注意在專案的 POM 檔案中使用 **maven-shade-plugin**。此外掛程式負責建立包含所需相依性的 JAR。如果您嘗試在沒有此外掛程式的情況下封裝專案，則所需的相依性不會包含在 JAR 檔案中，而且您會遇到 **ClassNotFoundException**。

## 部署 Lambda 函數。
<a name="lambda-s3-tutorial-deploy"></a>

1. 開啟 [Lambda 主控台](https://console.aws.amazon.com/lambda/home)。

1. 選擇 **Create Function (建立函數)**。

1. 選擇**從頭開始撰寫**。

1. 在**基本資訊**區段中，輸入 **Cron** 做為名稱。

1. 在**執行期**中，選擇 **Java 8**。

1. 選擇**使用現有角色**，然後選擇 **Lambda支援** (建立的 IAM 角色)。

1. 選擇**建立函數**。

1. 針對**程式碼專案類型**，選擇**上傳 .zip 檔案**。

1. 選擇**上傳**，然後瀏覽至您建立的 JAR 檔案。

1. 針對**處理程式**，請輸入該函數的完整名稱，例如：**com.example.tags.Handler:handleRequest** (**com.example.tags**會指定套件，**處理程式是**後面接著::和方法名稱的類別)。

1. 選擇**儲存**。

## 測試 Lambda 方法
<a name="lambda-s3-tutorial-test"></a>

在教學課程中，您可以測試 Lambda 函數。

1. 在 Lambda 主控台中，點擊**測試**索引標籤，然後輸入下列 JSON。

   ```
                    {
   "flag": "true"
    }
   ```  
![具有旗標索引鍵/值對、刪除和格式化按鈕以及叫用按鈕的測試事件 JSON 編輯器。](http://docs.aws.amazon.com/zh_tw/rekognition/latest/dg/images/v2-image-tutorial-test.png)
**注意**  
傳遞**真**索引標籤的數字資產和傳遞**假**刪除標籤。

1. 選擇**叫用**按鈕。調用 Lambda 函數之後，您會看到成功的訊息。  
![執行結果訊息指出操作成功，並顯示詳細資訊按鈕。](http://docs.aws.amazon.com/zh_tw/rekognition/latest/dg/images/v2-image-tutorial-success.png)

恭喜您，您已建立 AWS Lambda函數，以自動方式將標籤套用至位於 Amazon S3 儲存貯體中的二進制資產。如同本教學課程開頭所述，請務必終止您在進行本教學課程時所建立的資源，以確保不會產生費用。

如需更多AWS多服務範例，請參閱 [AWS文件開發套件範例 GitHub 儲存庫](https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javav2/usecases)。