From 0d1a4ec7ea32842ce406adcf04c6ffd59dd3377f Mon Sep 17 00:00:00 2001 From: jendib Date: Sun, 13 Dec 2015 22:29:23 +0100 Subject: [PATCH] #55: Export document in PDF (utilities) --- .../com/sismics/docs/core/model/jpa/File.java | 94 +++---------- .../com/sismics/docs/core/util/FileUtil.java | 123 ++++++++++++++++-- .../docs/core/util/TemporaryFileStream.java | 55 ++++++++ .../java/com/sismics/util/mime/MimeType.java | 3 - .../com/sismics/util/mime/MimeTypeUtil.java | 4 - .../docs/core/util/TestEncryptUtil.java | 10 +- .../sismics/docs/core/util/TestFileUtil.java | 57 ++++++-- .../test/resources/file/apollo_landscape.jpg | Bin 0 -> 23134 bytes .../test/resources/file/apollo_portrait.jpg | Bin 0 -> 7907 bytes docs-parent/pom.xml | 2 +- .../docs/rest/resource/FileResource.java | 2 +- 11 files changed, 239 insertions(+), 111 deletions(-) create mode 100644 docs-core/src/main/java/com/sismics/docs/core/util/TemporaryFileStream.java create mode 100644 docs-core/src/test/resources/file/apollo_landscape.jpg create mode 100644 docs-core/src/test/resources/file/apollo_portrait.jpg diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java index 401e1f7e..64682bc6 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java +++ b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java @@ -7,6 +7,7 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.Table; +import javax.persistence.Transient; import com.google.common.base.MoreObjects; @@ -69,150 +70,85 @@ public class File implements Loggable { private Integer order; /** - * Getter of id. - * - * @return the id + * Private key to decrypt the file. + * Not saved to database, of course. */ + @Transient + private String privateKey; + public String getId() { return id; } - /** - * Setter of id. - * - * @param id id - */ public void setId(String id) { this.id = id; } - /** - * Getter of documentId. - * - * @return the documentId - */ public String getDocumentId() { return documentId; } - /** - * Setter of documentId. - * - * @param documentId documentId - */ public void setDocumentId(String documentId) { this.documentId = documentId; } - - /** - * Getter of mimeType. - * - * @return the mimeType - */ + public String getMimeType() { return mimeType; } - /** - * Setter of mimeType. - * - * @param mimeType mimeType - */ public void setMimeType(String mimeType) { this.mimeType = mimeType; } - /** - * Getter of createDate. - * - * @return the createDate - */ public Date getCreateDate() { return createDate; } - /** - * Setter of createDate. - * - * @param createDate createDate - */ public void setCreateDate(Date createDate) { this.createDate = createDate; } - /** - * Getter of deleteDate. - * - * @return the deleteDate - */ @Override public Date getDeleteDate() { return deleteDate; } - /** - * Setter of deleteDate. - * - * @param deleteDate deleteDate - */ public void setDeleteDate(Date deleteDate) { this.deleteDate = deleteDate; } - /** - * Getter of content. - * - * @return the content - */ public String getContent() { return content; } - /** - * Setter of content. - * - * @param content content - */ public void setContent(String content) { this.content = content; } - /** - * Getter of order. - * - * @return the order - */ public Integer getOrder() { return order; } - /** - * Setter of order. - * - * @param order order - */ public void setOrder(Integer order) { this.order = order; } - /** - * Getter of userId. - * - * @return the userId - */ public String getUserId() { return userId; } - /** - * Setter of userId. - * - * @param userId userId - */ public void setUserId(String userId) { this.userId = userId; } + public String getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java index 59b30bec..82f9de31 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java +++ b/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java @@ -8,13 +8,22 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.imageio.ImageIO; +import org.apache.pdfbox.io.MemoryUsageSetting; +import org.apache.pdfbox.multipdf.PDFMergerUtility; import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory; +import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.text.PDFTextStripper; import org.apache.poi.xwpf.usermodel.XWPFDocument; @@ -27,6 +36,7 @@ import org.odftoolkit.odfdom.doc.OdfTextDocument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.io.Closer; import com.sismics.docs.core.model.jpa.Document; import com.sismics.docs.core.model.jpa.File; import com.sismics.tess4j.Tesseract; @@ -130,23 +140,24 @@ public class FileUtil { /** * Convert a file to PDF if necessary. * - * @param inputStream InputStream * @param file File + * @param inputStream InputStream + * @param reset Reset the stream after usage * @return PDF input stream * @throws Exception */ - public static InputStream convertToPdf(InputStream inputStream, File file) throws Exception { + public static InputStream convertToPdf(File file, InputStream inputStream, boolean reset) throws Exception { if (file.getMimeType().equals(MimeType.APPLICATION_PDF)) { // It's already PDF, just return the input return inputStream; } if (file.getMimeType().equals(MimeType.OFFICE_DOCUMENT)) { - return convertOfficeDocument(inputStream); + return convertOfficeDocument(inputStream, reset); } if (file.getMimeType().equals(MimeType.OPEN_DOCUMENT_TEXT)) { - return convertOpenDocumentText(inputStream); + return convertOpenDocumentText(inputStream, reset); } // PDF conversion not necessary/possible @@ -157,15 +168,18 @@ public class FileUtil { * Convert an open document text file to PDF. * * @param inputStream Unencrypted input stream + * @param reset Reset the stream after usage * @return PDF input stream * @throws Exception */ - private static InputStream convertOpenDocumentText(InputStream inputStream) throws Exception { + private static InputStream convertOpenDocumentText(InputStream inputStream, boolean reset) throws Exception { ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream(); OdfTextDocument document = OdfTextDocument.loadDocument(inputStream); PdfOptions options = PdfOptions.create(); PdfConverter.getInstance().convert(document, pdfOutputStream, options); - inputStream.reset(); + if (reset) { + inputStream.reset(); + } return new ByteArrayInputStream(pdfOutputStream.toByteArray()); } @@ -173,15 +187,18 @@ public class FileUtil { * Convert an Office document to PDF. * * @param inputStream Unencrypted input stream + * @param reset Reset the stream after usage * @return PDF input stream * @throws Exception */ - private static InputStream convertOfficeDocument(InputStream inputStream) throws Exception { + private static InputStream convertOfficeDocument(InputStream inputStream, boolean reset) throws Exception { ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream(); XWPFDocument document = new XWPFDocument(inputStream); org.apache.poi.xwpf.converter.pdf.PdfOptions options = org.apache.poi.xwpf.converter.pdf.PdfOptions.create(); org.apache.poi.xwpf.converter.pdf.PdfConverter.getInstance().convert(document, pdfOutputStream, options); - inputStream.reset(); + if (reset) { + inputStream.reset(); + } return new ByteArrayInputStream(pdfOutputStream.toByteArray()); } @@ -272,4 +289,94 @@ public class FileUtil { Files.delete(thumbnailFile); } } + + /** + * Convert a document and its files to a merged PDF file. + * + * @param fileList List of files + * @param fitImageToPage Fill images to the page + * @param margin Margins in millimeters + * @return PDF input stream + * @throws IOException + */ + public static InputStream convertToPdf(List fileList, boolean fitImageToPage, int margin) throws Exception { + // TODO PDF Export: Option to add a front page with: + // document title, document description, creator, date created, language, + // list of all files (and information if it is in this document or not) + // TODO PDF Export: Option to add the comments + + // Create a blank PDF + Closer closer = Closer.create(); + MemoryUsageSetting memUsageSettings = MemoryUsageSetting.setupMixed(1000000); // 1MB max memory usage + memUsageSettings.setTempDir(new java.io.File(System.getProperty("java.io.tmpdir"))); // To OS temp + float mmPerInch = 1 / (10 * 2.54f) * 72f; + + try (PDDocument doc = new PDDocument(memUsageSettings)) { + // Add files + for (File file : fileList) { + Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId()); + try (InputStream storedFileInputStream = file.getPrivateKey() == null ? // Try to decrypt the file if we have a private key available + Files.newInputStream(storedFile) : EncryptionUtil.decryptInputStream(Files.newInputStream(storedFile), file.getPrivateKey())) { + if (ImageUtil.isImage(file.getMimeType())) { + PDPage page = new PDPage(PDRectangle.A4); // Images into A4 pages + try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) { + // Read the image using the correct handler. PDFBox can't do it because it relies wrongly on file extension + PDImageXObject pdImage = null; + if (file.getMimeType().equals(MimeType.IMAGE_JPEG)) { + pdImage = JPEGFactory.createFromStream(doc, storedFileInputStream); + } else if (file.getMimeType().equals(MimeType.IMAGE_GIF) || file.getMimeType().equals(MimeType.IMAGE_PNG)) { + BufferedImage bim = ImageIO.read(storedFileInputStream); + pdImage = LosslessFactory.createFromImage(doc, bim); + } + + if (fitImageToPage) { + // Fill the page with the image + float widthAvailable = page.getMediaBox().getWidth() - 2 * margin * mmPerInch; + float heightAvailable = page.getMediaBox().getHeight() - 2 * margin * mmPerInch; + + // Compare page format and image format + if (widthAvailable / heightAvailable < (float) pdImage.getWidth() / (float) pdImage.getHeight()) { + float imageHeight = widthAvailable / pdImage.getWidth() * pdImage.getHeight(); + contentStream.drawImage(pdImage, margin * mmPerInch, heightAvailable + margin * mmPerInch - imageHeight, + widthAvailable, imageHeight); + } else { + float imageWidth = heightAvailable / pdImage.getHeight() * pdImage.getWidth(); + contentStream.drawImage(pdImage, margin * mmPerInch, margin * mmPerInch, + imageWidth, heightAvailable); + } + } else { + // Draw the image as is + contentStream.drawImage(pdImage, margin * mmPerInch, + page.getMediaBox().getHeight() - pdImage.getHeight() - margin * mmPerInch); + } + } + doc.addPage(page); + } else { + // Try to convert the file to PDF + InputStream pdfInputStream = convertToPdf(file, storedFileInputStream, false); + if (pdfInputStream != null) { + // This file is convertible to PDF, just add it to the end + try { + PDDocument mergeDoc = PDDocument.load(pdfInputStream, memUsageSettings); + closer.register(mergeDoc); + PDFMergerUtility pdfMergerUtility = new PDFMergerUtility(); + pdfMergerUtility.appendDocument(doc, mergeDoc); + } finally { + pdfInputStream.close(); + } + } + + // All other non-PDF-convertible files are ignored + } + } + } + + // Save to a temporary file + try (TemporaryFileStream temporaryFileStream = new TemporaryFileStream()) { + doc.save(temporaryFileStream.openWriteStream()); + closer.close(); // Close all remaining opened PDF + return temporaryFileStream.openReadStream(); + } + } + } } diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/TemporaryFileStream.java b/docs-core/src/main/java/com/sismics/docs/core/util/TemporaryFileStream.java new file mode 100644 index 00000000..9b357f05 --- /dev/null +++ b/docs-core/src/main/java/com/sismics/docs/core/util/TemporaryFileStream.java @@ -0,0 +1,55 @@ +package com.sismics.docs.core.util; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.UUID; + +/** + * Utilities for writing and reading to a temporary file. + * + * @author bgamard + */ +public class TemporaryFileStream implements Closeable { + /** + * Temporary file. + */ + private Path tempFile; + + /** + * Construct a temporary file. + * + * @throws IOException + */ + public TemporaryFileStream() throws IOException { + tempFile = Files.createTempFile(UUID.randomUUID().toString(), ".tmp"); + } + + /** + * Open a stream for writing. + * + * @return OutputStream + * @throws IOException + */ + public OutputStream openWriteStream() throws IOException { + return Files.newOutputStream(tempFile); + } + + /** + * Open a stream for reading. + * + * @return InputStream + * @throws IOException + */ + public InputStream openReadStream() throws IOException { + return Files.newInputStream(tempFile); + } + + @Override + public void close() throws IOException { + Files.delete(tempFile); + } +} \ No newline at end of file diff --git a/docs-core/src/main/java/com/sismics/util/mime/MimeType.java b/docs-core/src/main/java/com/sismics/util/mime/MimeType.java index e5821a62..b85517ef 100644 --- a/docs-core/src/main/java/com/sismics/util/mime/MimeType.java +++ b/docs-core/src/main/java/com/sismics/util/mime/MimeType.java @@ -6,9 +6,6 @@ package com.sismics.util.mime; * @author jtremeaux */ public class MimeType { - - public static final String IMAGE_X_ICON = "image/x-icon"; - public static final String IMAGE_PNG = "image/png"; public static final String IMAGE_JPEG = "image/jpeg"; diff --git a/docs-core/src/main/java/com/sismics/util/mime/MimeTypeUtil.java b/docs-core/src/main/java/com/sismics/util/mime/MimeTypeUtil.java index 058f7f7d..fcb3d2ec 100644 --- a/docs-core/src/main/java/com/sismics/util/mime/MimeTypeUtil.java +++ b/docs-core/src/main/java/com/sismics/util/mime/MimeTypeUtil.java @@ -55,8 +55,6 @@ public class MimeTypeUtil { } else if (headerBytes[0] == ((byte) 0x89) && headerBytes[1] == ((byte) 0x50) && headerBytes[2] == ((byte) 0x4e) && headerBytes[3] == ((byte) 0x47) && headerBytes[4] == ((byte) 0x0d) && headerBytes[5] == ((byte) 0x0a) && headerBytes[6] == ((byte) 0x1a) && headerBytes[7] == ((byte) 0x0a)) { return MimeType.IMAGE_PNG; - } else if (headerBytes[0] == ((byte) 0x00) && headerBytes[1] == ((byte) 0x00) && headerBytes[2] == ((byte) 0x01) && headerBytes[3] == ((byte) 0x00)) { - return MimeType.IMAGE_X_ICON; } else if (headerBytes[0] == ((byte) 0x25) && headerBytes[1] == ((byte) 0x50) && headerBytes[2] == ((byte) 0x44) && headerBytes[3] == ((byte) 0x46)) { return MimeType.APPLICATION_PDF; } @@ -80,8 +78,6 @@ public class MimeTypeUtil { return "jpg"; case MimeType.IMAGE_PNG: return "png"; - case MimeType.IMAGE_X_ICON: - return "ico"; case MimeType.APPLICATION_PDF: return "pdf"; case MimeType.OPEN_DOCUMENT_TEXT: diff --git a/docs-core/src/test/java/com/sismics/docs/core/util/TestEncryptUtil.java b/docs-core/src/test/java/com/sismics/docs/core/util/TestEncryptUtil.java index b6f54964..39477e5f 100644 --- a/docs-core/src/test/java/com/sismics/docs/core/util/TestEncryptUtil.java +++ b/docs-core/src/test/java/com/sismics/docs/core/util/TestEncryptUtil.java @@ -18,11 +18,6 @@ import com.google.common.io.ByteStreams; * @author bgamard */ public class TestEncryptUtil { - /** - * Test private key. - */ - String pk = "OnceUponATime"; - @Test public void generatePrivateKeyTest() throws Exception { String key = EncryptionUtil.generatePrivateKey(); @@ -38,7 +33,7 @@ public class TestEncryptUtil { } catch (IllegalArgumentException e) { // NOP } - Cipher cipher = EncryptionUtil.getEncryptionCipher(pk); + Cipher cipher = EncryptionUtil.getEncryptionCipher("OnceUponATime"); InputStream inputStream = new CipherInputStream(this.getClass().getResourceAsStream("/file/udhr.pdf"), cipher); byte[] encryptedData = ByteStreams.toByteArray(inputStream); byte[] assertData = ByteStreams.toByteArray(this.getClass().getResourceAsStream("/file/udhr_encrypted.pdf")); @@ -48,7 +43,8 @@ public class TestEncryptUtil { @Test public void decryptStreamTest() throws Exception { - InputStream inputStream = EncryptionUtil.decryptInputStream(this.getClass().getResourceAsStream("/file/udhr_encrypted.pdf"), pk); + InputStream inputStream = EncryptionUtil.decryptInputStream( + this.getClass().getResourceAsStream("/file/udhr_encrypted.pdf"), "OnceUponATime"); byte[] encryptedData = ByteStreams.toByteArray(inputStream); byte[] assertData = ByteStreams.toByteArray(this.getClass().getResourceAsStream("/file/udhr.pdf")); diff --git a/docs-core/src/test/java/com/sismics/docs/core/util/TestFileUtil.java b/docs-core/src/test/java/com/sismics/docs/core/util/TestFileUtil.java index 1482d88c..e8dff58a 100644 --- a/docs-core/src/test/java/com/sismics/docs/core/util/TestFileUtil.java +++ b/docs-core/src/test/java/com/sismics/docs/core/util/TestFileUtil.java @@ -1,13 +1,14 @@ package com.sismics.docs.core.util; -import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import junit.framework.Assert; -import org.apache.pdfbox.io.IOUtils; import org.junit.Test; +import com.google.common.collect.Lists; import com.google.common.io.Resources; import com.sismics.docs.core.model.jpa.File; import com.sismics.util.mime.MimeType; @@ -20,11 +21,10 @@ import com.sismics.util.mime.MimeType; public class TestFileUtil { @Test public void extractContentOpenDocumentTextTest() throws Exception { - try (InputStream inputStream = Resources.getResource("file/document.odt").openStream(); - InputStream bytesInputStream = new ByteArrayInputStream(IOUtils.toByteArray(inputStream))) { + try (InputStream inputStream = Resources.getResource("file/document.odt").openStream()) { File file = new File(); file.setMimeType(MimeType.OPEN_DOCUMENT_TEXT); - try (InputStream pdfInputStream = FileUtil.convertToPdf(bytesInputStream, file)) { + try (InputStream pdfInputStream = FileUtil.convertToPdf(file, inputStream, false)) { String content = FileUtil.extractContent(null, file, inputStream, pdfInputStream); Assert.assertTrue(content.contains("Lorem ipsum dolor sit amen.")); } @@ -33,14 +33,55 @@ public class TestFileUtil { @Test public void extractContentOfficeDocumentTest() throws Exception { - try (InputStream inputStream = Resources.getResource("file/document.docx").openStream(); - InputStream bytesInputStream = new ByteArrayInputStream(IOUtils.toByteArray(inputStream))) { + try (InputStream inputStream = Resources.getResource("file/document.docx").openStream()) { File file = new File(); file.setMimeType(MimeType.OFFICE_DOCUMENT); - try (InputStream pdfInputStream = FileUtil.convertToPdf(bytesInputStream, file)) { + try (InputStream pdfInputStream = FileUtil.convertToPdf(file, inputStream, false)) { String content = FileUtil.extractContent(null, file, inputStream, pdfInputStream); Assert.assertTrue(content.contains("Lorem ipsum dolor sit amen.")); } } } + + @Test + public void convertToPdfTest() throws Exception { + try (InputStream inputStream0 = Resources.getResource("file/apollo_landscape.jpg").openStream(); + InputStream inputStream1 = Resources.getResource("file/apollo_portrait.jpg").openStream(); + InputStream inputStream2 = Resources.getResource("file/udhr_encrypted.pdf").openStream(); + InputStream inputStream3 = Resources.getResource("file/document.docx").openStream(); + InputStream inputStream4 = Resources.getResource("file/document.odt").openStream()) { + // First file + Files.copy(inputStream0, DirectoryUtil.getStorageDirectory().resolve("apollo_landscape"), StandardCopyOption.REPLACE_EXISTING); + File file0 = new File(); + file0.setId("apollo_landscape"); + file0.setMimeType(MimeType.IMAGE_JPEG); + + // Second file + Files.copy(inputStream1, DirectoryUtil.getStorageDirectory().resolve("apollo_portrait"), StandardCopyOption.REPLACE_EXISTING); + File file1 = new File(); + file1.setId("apollo_portrait"); + file1.setMimeType(MimeType.IMAGE_JPEG); + + // Third file + Files.copy(inputStream2, DirectoryUtil.getStorageDirectory().resolve("udhr"), StandardCopyOption.REPLACE_EXISTING); + File file2 = new File(); + file2.setId("udhr"); + file2.setPrivateKey("OnceUponATime"); + file2.setMimeType(MimeType.APPLICATION_PDF); + + // Fourth file + Files.copy(inputStream3, DirectoryUtil.getStorageDirectory().resolve("document_docx"), StandardCopyOption.REPLACE_EXISTING); + File file3 = new File(); + file3.setId("document_docx"); + file3.setMimeType(MimeType.OFFICE_DOCUMENT); + + // Fifth file + Files.copy(inputStream4, DirectoryUtil.getStorageDirectory().resolve("document_odt"), StandardCopyOption.REPLACE_EXISTING); + File file4 = new File(); + file4.setId("document_odt"); + file4.setMimeType(MimeType.OPEN_DOCUMENT_TEXT); + + FileUtil.convertToPdf(Lists.newArrayList(file0, file1, file2, file3, file4), true, 10).close(); + } + } } diff --git a/docs-core/src/test/resources/file/apollo_landscape.jpg b/docs-core/src/test/resources/file/apollo_landscape.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aae2daf817c246ba03d037ed90c77148adcb5ca3 GIT binary patch literal 23134 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 literal 0 HcmV?d00001 diff --git a/docs-parent/pom.xml b/docs-parent/pom.xml index 40da3594..cf977d0b 100644 --- a/docs-parent/pom.xml +++ b/docs-parent/pom.xml @@ -30,7 +30,7 @@ 0.3m 4.2.0 4.2 - 2.0.0-RC1 + 2.0.0-RC2 1.53 2.9.1 4.1.0.Final diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java index 4b37e080..539f79e2 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java @@ -150,7 +150,7 @@ public class FileResource extends BaseResource { file.setMimeType(MimeTypeUtil.guessOpenDocumentFormat(file, fileInputStream)); // Convert to PDF if necessary (for thumbnail and text extraction) - InputStream pdfIntputStream = FileUtil.convertToPdf(fileInputStream, file); + InputStream pdfIntputStream = FileUtil.convertToPdf(file, fileInputStream, true); // Save the file FileUtil.save(fileInputStream, pdfIntputStream, file, user.getPrivateKey());