2016-02-04 19 views
5

AWSラムダ関数のJavaでバイトストリーミングクライアントを作成しようとしています。私は、RequestStreamHandlerの実装としてラムダ関数を作成しました。このプロジェクトの基礎は、文書hereに記載されています。AWSラムダバイトストリーミングクライアント(Java)

public class LambdaFunctionHandler implements RequestStreamHandler { 
    static final String bucket = "anS3bucket"; 
    static final String key = "anS3KeyToAJpegFile"; 

    @Override 
    public void handleRequest(InputStream input, OutputStream output, 
      Context context) throws IOException { 

     AmazonS3 s3Client = new AmazonS3Client(
       new EnvironmentVariableCredentialsProvider()); 
     try { 
      context.getLogger().log("Downloading an object\n"); 
      S3Object s3object = s3Client.getObject(new GetObjectRequest(
        bucket, key)); 
      context.getLogger().log("Content-Type: " + 
        s3object.getObjectMetadata().getContentType() 
         + "\n"); 
      InputStream in = s3object.getObjectContent(); 
      int b = 0; 
      context.getLogger().log("Writing jpeg on output\n"); 
      while ((b = in.read()) > -1) { 
       output.write(b); 
      } 

     } catch (AmazonServiceException e) { 
      System.out.println("Error Message: " + e.getMessage()); 
     } 
    } 
} 

このハードコーディングされた例は、Lambdaテストコンソールで正常に動作します。 JARをアップロードし、ラムダ関数を実行することができます( "Test"をクリックしてください)。この関数の機能は、jpegファイルの内容を取得し、バイトストリームをOutputStreamに書き出すことです。私は関数の結果としてテストコンソールでバイナリ出力を見ることができます。これまでのところすべてが問題ありません。最終的に私はJPEG上でImageMagickを実行し、サイズを変更します - これはこのプロジェクトの目標です。

私のクライアントコードは次のようになります。私は、Javaクライアントでバイトストリームを受け取るしようとすると

public interface ImageService { 
    @LambdaFunction(functionName="ImageProcessing") 
    OutputStream getImageStream(InputStream data); 
} 

public class LambdaImageTest { 

public static void main(String[] args) throws IOException { 
    AWSLambdaClient lambda = new AWSLambdaClient(new ProfileCredentialsProvider()); 
    lambda.configureRegion(Regions.EU_WEST_1); 

    ImageService service = LambdaInvokerFactory.build(ImageService.class, lambda); 

    // Call lambda function, receive byte stream 
    OutputStream out = service.getImageStream(null); 
    System.out.println(out); // This code is not complete 
} 

、私は失敗します。バイトストリームを受け取る方法はないようです。クライアントは、私がここで望んでいるものではない、jsonデータとしてリルートを読み取ろうとしているようだ。私は直接バイトストリーム(jpegバイナリの内容)を読み取るしたいです。私が得るエラーは:

Exception in thread "main" com.amazonaws.services.lambda.invoke.LambdaSerializationException: Failed to parse Lambda function result 
    at com.amazonaws.services.lambda.invoke.LambdaInvokerFactory$LambdaInvocationHandler.getObjectFromPayload(LambdaInvokerFactory.java:210) 
    at com.amazonaws.services.lambda.invoke.LambdaInvokerFactory$LambdaInvocationHandler.processInvokeResult(LambdaInvokerFactory.java:189) 
    at com.amazonaws.services.lambda.invoke.LambdaInvokerFactory$LambdaInvocationHandler.invoke(LambdaInvokerFactory.java:106) 
    at com.sun.proxy.$Proxy3.getImageStream(Unknown Source) 
    at se.devo.lambda.image.LambdaImageTest.main(LambdaImageTest.java:33) 
Caused by: com.fasterxml.jackson.core.JsonParseException: Invalid UTF-8 middle byte 0xff 
at [Source: [[email protected]; line: 1, column: 4] 
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1487) 

AWS Lambda Javaクライアントでバイトストリームデータを正しく受信するにはどうすればよいですか?

答えて

2

解決策を見つけました。 LambdaInvokerFactoryクラスは、要求と応答のデータをJSONとして常に処理するため、問題のシリアル化とデシリアライズを行います。ソースコードは答えの手掛かりを保持していますが、ラムダ関数の呼び出しを行う部分を切り取っていますが、JSONの逆シリアル化をバイパスしてペイロードに直接アクセスします。シンプルですが、実際にはLambdaInvokerFactoryクラスにあったはずです...

ここに私の完全に働く解決策があります。ラムダ関数コード:

public class LambdaFunctionHandler implements RequestStreamHandler { 
    public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { 
     ObjectMapper mapper = new ObjectMapper(); 
     AmazonS3 s3Client = new AmazonS3Client(
       new EnvironmentVariableCredentialsProvider()); 
     try { 

      // Need to deserialize JSON data ourselves in Lambda streaming mode 
      String data = getJSONInputStream(input); 
      context.getLogger().log("JSON data:\n'" + data + "'\n");    
      context.getLogger().log("Deserialize JSON data to object\n"); 
      ImageRequest request = mapper.readValue(data, ImageRequest.class); 

      context.getLogger().log(String.format("Downloading S3 object: %s %s\n", 
        request.getBucket(), request.getKey())); 
      S3Object s3object = s3Client.getObject(new GetObjectRequest(
        request.getBucket(), request.getKey())); 
      context.getLogger().log("Content-Type: " + 
        s3object.getObjectMetadata().getContentType() + "\n"); 
      InputStream in = s3object.getObjectContent(); 
      int b = 0; 
      byte[] buf = new byte[2048]; 
      context.getLogger().log("Writing image on output\n"); 
      while ((b = in.read(buf)) > -1) { 
       output.write(buf, 0, b); 
      } 

     } catch (AmazonServiceException e) { 
      System.out.println("Error Message: " + e.getMessage()); 
     } 
    } 

    private String getJSONInputStream(InputStream input) throws IOException { 
     BufferedReader reader = new BufferedReader(new InputStreamReader(input)); 
     String data = ""; 
     String line; 
     while ((line = reader.readLine()) != null) { 
       data += line; 
     } 
     return data; 
    } 
} 

クライアントコード:

public class LambdaImageTest { 
    private static final ObjectMapper MAPPER = new ObjectMapper(); 

    public static void main(String[] args) throws IOException { 
     String bucketName = args[0]; 
     String key  = args[1]; 

     // Lambda client proxy 
     AWSLambdaClient lambda = new AWSLambdaClient(new ProfileCredentialsProvider()); 
     lambda.configureRegion(Regions.EU_WEST_1); 

     // Build InvokeRequest 
     InvokeRequest invokeRequest = buildInvokeRequest("ImageProcessing", 
       new ImageRequest(bucketName, key)); 

     // Invoke and get result payload as ByteBuffer. Note error handling should be done here 
     InvokeResult invokeResult = lambda.invoke(invokeRequest); 
     ByteBuffer byteBuffer = invokeResult.getPayload(); 

     // Write payload to file. Output hardcoded... 
     FileChannel out = new FileOutputStream("D:/test.jpg").getChannel(); 
     out.write(byteBuffer); 
     out.close(); 
    } 

    private static InvokeRequest buildInvokeRequest(String functionName, Object input) { 

     InvokeRequest invokeRequest = new InvokeRequest(); 
     invokeRequest.setFunctionName(functionName); // Lambda function name identifier 
     invokeRequest.setInvocationType(InvocationType.RequestResponse); 
     invokeRequest.setLogType(LogType.None); 

     if (input != null) { 
      try { 

       String payload = MAPPER.writer().writeValueAsString(input); 
       invokeRequest.setPayload(payload); 

      } catch (JsonProcessingException ex) { 
       throw new LambdaSerializationException("Failed to serialize request object to JSON", ex); 
      } 
     } 

     return invokeRequest; 
    } 
} 

一つは、そのエラー処理に注意しなければならないが、ここで改善する必要があります。 LambdaInvokerFactoryのソースは欠けている部分を保持しています。

0

ImageServiceインターフェイスでは、ストリームではなくオブジェクトタイプを使用する必要があります。たとえば、次のように試してみてください。

public interface ImageService { 
    @LambdaFunction(functionName="ImageProcessing") 
    byte[] getImageStream(byte[] data); 
} 
+0

申し訳ありませんが、まったく同じエラーが表示されます。 LambdaInvokerFactoryを使用すると、要求と応答の両方のデータの中間形式としてJSONが使用されるようです。サーバ側でラムダ関数の変種として文書化されているストリーミングモデルは、現時点ではクライアントサイドではサポートされていないようです。 – Barsk

関連する問題