2017-10-17 11 views
0

Le seau AWS S3 possède un fichier pdf. Le contenu de ce fichier pdf doit être édité en utilisant la bibliothèque Java iText. Le fichier modifié doit être stocké à nouveau dans le compartiment S3. Actuellement, nous utilisons la fonction AWS Lambda. Fichier vide pdf est obtenir créé dans le seau s3 de destination avec un message d'erreur dans AWS CloudWatch: "tuyau fermé"Modifier le fichier pdf dans le seau AWS S3 en utilisant iText

Lambda Code java:

private String bucketName = "forms-storage"; 

public String getProposalPdf(InputRequest inputRequest, Context context) throws DocumentException, IOException{ 

    final BasicAWSCredentials awsCreds = new BasicAWSCredentials(ConstantValues.AccessKey, ConstantValues.SecretKey); 
    final AmazonS3Client s3client = (AmazonS3Client) AmazonS3ClientBuilder.standard().withRegion(Regions.AP_SOUTH_1) 
        .withCredentials(new AWSStaticCredentialsProvider(awsCreds)).build(); 
    S3Object object = s3client.getObject(new GetObjectRequest(bucketName, "forms/COMBO ver 1.1.pdf")); 
    InputStream objectData = object.getObjectContent(); 

    PdfReader reader; 
    PdfStamper stamper = null; 
    BaseFont bf; 

    PipedOutputStream pdfBytes = new PipedOutputStream(); 

    try {   
     reader = new PdfReader(objectData); 
     stamper = new PdfStamper(reader, pdfBytes); 

     bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); 

     PdfContentByte over = stamper.getOverContent(1); 
     over.beginText(); 
     over.setColorFill(BaseColor.BLACK); 
     over.setFontAndSize(bf, 12); 
     over.setTextMatrix(120,717); 
     over.showText("this is edited text"); 
     over.endText(); 

     PipedInputStream inputStream = new PipedInputStream(pdfBytes); 

     ObjectMetadata meta = new ObjectMetadata(); 
     meta= object.getObjectMetadata(); 
     meta.setContentLength(inputStream.available());   

     s3client.putObject(new PutObjectRequest(bucketName, "forms/123.pdf", inputStream, meta));   

    } catch (IOException e) { 
     e.printStackTrace(); 
    } catch (DocumentException e) { 
     e.printStackTrace(); 
    } 
    finally 
    { 
     stamper.close();    
     objectData.close(); 
    } 
    return "PDF Created"; 
} 

Répondre

0

Le problème est pas dans AWS ou iText, mais il est en la façon dont vous traitez PipedInputStream et PipedOutputStream.

En particulier, la plupart des données importantes sont écrites dans le PDF lorsque stamper.close() est appelée, mais vous définissez la longueur du contenu meta.setContentLength(inputStream.available()); avant de fermer la matrice, la longueur n'est donc pas valide. Après avoir appelé putObject, l'instance inputStream est fermée (vérifiez le champ interne closedByReader), mais pdfBytes reste connecté et ne peut pas y écrire après inputStream est fermé, donc lorsque stamper.close(); est appelée, vous obtenez une exception car vous n'êtes pas en mesure d'écrire à inputStream plus.

Je ne pense pas que toute tentative de résoudre ce problème dans l'approche actuelle sera suffisante parce que dans le documentation il est clairement indiqué que

En règle générale, les données sont lues à partir d'un objet PipedInputStream par un fil et des données est écrit dans le PipedOutputStream correspondant par un autre thread. Il est déconseillé d'essayer d'utiliser les deux objets à partir d'un seul thread, car cela peut bloquer le thread.

Donc, une solution serait, mais pas si efficace de la mémoire, d'utiliser ByteArrayOutputStream et ByteArrayInputStream:

ByteArrayOutputStream pdfBytes = new ByteArrayOutputStream(); 

try { 
    reader = new PdfReader(objectData); 
    stamper = new PdfStamper(reader, pdfBytes); 

    bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); 

    PdfContentByte over = stamper.getOverContent(1); 
    over.beginText(); 
    over.setColorFill(BaseColor.BLACK); 
    over.setFontAndSize(bf, 12); 
    over.setTextMatrix(120,717); 
    over.showText("this is edited text"); 
    over.endText(); 

    stamper.close(); 
    objectData.close(); 

    ObjectMetadata meta = new ObjectMetadata(); 
    meta= object.getObjectMetadata(); 
    ByteArrayInputStream inputStream = new ByteArrayInputStream(pdfBytes.toByteArray()); 
    meta.setContentLength(inputStream.available()); 

    s3client.putObject(new PutObjectRequest(bucketName, "forms/123.pdf", inputStream, meta));  

} catch (IOException e) { 
    e.printStackTrace(); 
} catch (DocumentException e) { 
    e.printStackTrace(); 
} 

Généralement les fichiers PDF ne sont pas si énormes dans la taille de sorte que vous pouvez vous permettre de les stocker dans la mémoire . Si vous souhaitez optimiser la consommation de mémoire, vous devez effectuer le traitement PDF dans un fil séparé. Je recommande de vérifier article this ou rechercher des exemples génériques d'utiliser PipedInputStream avec PipedOutputStream.