2016-02-04 13 views
5

Sto provando a scrivere un client di streaming byte in Java per una funzione AWS Lambda. Ho creato la funzione Lambda come implementazione di RequestStreamHandler. La base per questo progetto è descritta nei documenti here.AWS Client di streaming byte Lambda in 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()); 
     } 
    } 
} 

Questo esempio hardcoded funziona bene sulla console di test Lambda. Posso caricare il JAR ed eseguire la funzione lambda (facendo clic su "Test"). Quello che fa la funzione è che esso recuperi un contenuto di file jpeg e scriva il flusso di byte su OutputStream. Riesco a vedere l'output binario nella console di test come risultato della funzione. Finora tutto va bene. Alla fine eseguirò ImageMagick su jpeg e ridimensionarlo - questo è l'obiettivo di questo progetto.

Il mio codice cliente appare come:

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 
} 

Quando provo per ricevere il flusso di byte in un client Java, non riesco. Non sembra essere un modo per ricevere il flusso di byte. Il cliente sembra provare a leggere il reault come dati json, che non è quello che voglio qui. Voglio leggere direttamente il flusso di byte (il contenuto binario di jpeg). L'errore che ottengo è:

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) 

Come faccio a ricevere correttamente i dati flusso di byte in un client java AWS Lambda?

risposta

2

Ho trovato una soluzione. La classe LambdaInvokerFactory gestirà SEMPRE i dati di richiesta e di risposta come JSON e quindi serializzerà e deserializzerà quale è il problema. Tuttavia, il codice sorgente è l'indizio della risposta, e ho estratto la parte che fa l'invocazione della funzione lambda, ma ignoro la deserializzazione JSON e accedo direttamente al payload. Semplice, ma avrebbe dovuto essere già nella classe LambdaInvokerFactory ...

Ecco la mia soluzione pienamente funzionante. Il codice di funzione lambda:

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; 
    } 
} 

Il codice client:

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; 
    } 
} 

Si dovrebbe notare che la gestione degli errori deve essere migliorato qui. La fonte in LambdaInvokerFactory contiene i pezzi mancanti.

0

L'interfaccia ImageService deve utilizzare tipi di oggetto anziché flussi. Ad esempio, prova:

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

Siamo spiacenti, questo dà lo stesso errore. Sembrerebbe che tutti gli usi di LambdaInvokerFactory utilizzino JSON come modulo intermedio per entrambi i dati di richiesta e di risposta. Il modello di streaming che è documentato come variante della funzione Lambda sul lato server non sembra essere supportato sul lato client al momento, per quanto posso dire. – Barsk