在 Swift SOTO 中使用 AWS Rekognition

问题描述

我使用 devhr python lambda function 作为使用 Soto for AWS 的 Swift Lambda 函数的模型。当图像上传到 S3 并使用 AWS Rekognition 服务将标签(图像中对象的描述)添加到 DynamoDB 数据库时,Lambda 函数将由 S3 事件触发。我在使 rekFunction 功能正确时遇到问题,我希望有人可以建议如何使其正常工作。

到目前为止我编写的 Swift 代码

import AWSLambdaRuntime
import AWSLambdaEvents
import NIO
import Foundation
import sotoS3
import sotoRekognition
import sotoDynamoDB

struct Bucket: Decodable {
    let name: String
}

struct Object: Decodable {
    let key: String
}

struct MyS3: Decodable {
    let bucket: Bucket
    let object: Object
}

struct Record: Decodable {
    let s3: MyS3
}

struct Input: Decodable {
    let records: [Record]
}

struct Output: encodable {
    let result: String
}

struct MyHandler: EventLoopLambdaHandler {
    typealias In = APIGateway.V2.Request
    typealias Out = APIGateway.V2.Response
    
    let minConfidence: Float = 50

    let awsClient: AWSClient
    
    init(context: Lambda.InitializationContext) {
        self.awsClient = AWSClient(httpClientProvider: .createNewWithEventLoopGroup(context.eventLoop))
    }
    
    func shutdown(context: Lambda.ShutdownContext) -> EventLoopFuture<Void> {
        let promise = context.eventLoop.makePromise(of: Void.self)
        awsClient.shutdown { error in
            if let error = error {
                promise.fail(error)
            } else {
                promise.succeed(())
            }
        }
        return context.eventLoop.makeSucceededFuture(())
    }

    func handle(context: Lambda.Context,event: In) -> EventLoopFuture<Out> {
        guard let input: Input = try? event.bodyObject() else {
            return context.eventLoop.makeSucceededFuture(APIGateway.V2.Response(with: APIError.requestError,statusCode: .badRequest))
        }

        for record in input.records {
            let ourBucket = record.s3.bucket.name
            let ourKey = record.s3.object.key
            
            // For each message (photo) get the bucket name and key
            rekFunction(bucket: ourBucket,key: ourKey)
        }
        let output = Output(result: "Finished!")
        let apigatewayOutput = APIGateway.V2.Response(with: output,statusCode: .ok)
        return context.eventLoop.makeSucceededFuture(apigatewayOutput)
    }
        
    func rekFunction(bucket: String,key: String) {
        let safeKey = key.replacingOccurrences(of: "%3A",with: ":")
        print("Currently processing the following image")
        print("Bucket:",bucket," key name:",safeKey)
        var objectsDetected: [String] = []
        var imageLabels = [ "image": safeKey ]
        let s3Client = S3(client: awsClient,region: .euwest1)

        let s3Object = Rekognition.S3Object(bucket: bucket,name: safeKey)
        let image = Rekognition.Image(s3Object: s3Object)
        let rekognitionClient = Rekognition(client: awsClient)
        let detectLabelsRequest = Rekognition.DetectLabelsRequest(image: image,maxLabels: 10,minConfidence: minConfidence)
        rekognitionClient.detectLabels(detectLabelsRequest)
            .flatMap { detectLabelsResponse -> EventLoopFuture<Void> in
                if let labels = detectLabelsResponse.labels {
                    // Add all of our labels into imageLabels by iterating over response['Labels']
                    for label in labels {
                        if let name = label.name {
                            objectsDetected.append(name)
                            let itemAtt = "object\(objectsDetected.count)"
                            
                            // We Now have our shiny new item ready to put into DynamoDB
                            imageLabels[itemAtt] = name
                            
                            // Instantiate a table resource object of our environment variable
                            let imageLabelsTable = // Environment("TABLE") How can I read env vars?
                            let table = sotoDynamoDB.getTable(imageLabelsTable) // python: table = dynamodb.Table(imageLabelsTable)
                            // python: table.put_item(Item=imageLabels)
                        }
                    }
                }
                return ???
            }
        
    }
    
}
Lambda.run { MyHandler(context: $0) }

解决方法

这里有很多问题需要讨论。

  1. 当您从 S3 事件触发此操作时。 MyLambda.In 类型别名应为 S3.Event 并且鉴于没有任何等待结果 MyLambda.Out 应为 Void
  2. 您可以使用 Lambda.env("TABLE") 提取环境变量。
  3. 至于写入您的 DynamoDB 表,最好的方法可能是使用 Soto DynamoDB Codable 支持。在下面的示例中,我将假设您使用 s3 存储桶和密钥的组合作为密钥。
struct RekEntry: Codable {
    let key: String
    let labels: [String]
}
let entry = RekEntry(
    key: "\(bucket)/\(key)"
    labels: labels
)
let putRequest = DynamoDB.PutItemCodableInput(
    item: entry,tableName: Lambda.env("TABLE")
)
return dynamoDB.putItem(putRequest)
  1. 最后从 flatMap 返回什么。首先,您的函数 rekFunction 需要返回一个 EventLoopFuture<Void>,因为这是您从 Lambda 处理程序返回的内容。因此,您可以将前一点中 dynamoDB.putItem 的结果映射到 Void。
func rekFunction(bucket: String,key: String) -> EventLoopFuture<Void> {
    ...
    return rekognitionClient.detectLabels(detectLabelsRequest)
        .flatMap {
            ...
            return dynamoDB.putItem(putRequest)
        }
        .map { _ in }
}

我希望涵盖一切

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...