From fb18d0d04db51e5c255532872aa68f0f7aaead74 Mon Sep 17 00:00:00 2001 From: dkaitantzidis Date: Mon, 25 Dec 2023 22:36:08 +0200 Subject: [PATCH 1/2] WIP: Fixes issue - needs refactor --- .../SPDF/controller/api/MergeController.java | 161 +++++++++--------- 1 file changed, 85 insertions(+), 76 deletions(-) diff --git a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java index 4727e195..cc890ee9 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -1,7 +1,6 @@ package stirling.software.SPDF.controller.api; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; @@ -10,10 +9,15 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +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.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -34,82 +38,87 @@ public class MergeController { private static final Logger logger = LoggerFactory.getLogger(MergeController.class); -private PDDocument mergeDocuments(List documents) throws IOException { - PDDocument mergedDoc = new PDDocument(); - for (PDDocument doc : documents) { - for (PDPage page : doc.getPages()) { - mergedDoc.addPage(page); - } - } - return mergedDoc; -} - -private Comparator getSortComparator(String sortType) { - switch (sortType) { - case "byFileName": - return Comparator.comparing(MultipartFile::getOriginalFilename); - case "byDateModified": - return (file1, file2) -> { - try { - BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class); - BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class); - return attr1.lastModifiedTime().compareTo(attr2.lastModifiedTime()); - } catch (IOException e) { - return 0; // If there's an error, treat them as equal - } - }; - case "byDateCreated": - return (file1, file2) -> { - try { - BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class); - BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class); - return attr1.creationTime().compareTo(attr2.creationTime()); - } catch (IOException e) { - return 0; // If there's an error, treat them as equal - } - }; - case "byPDFTitle": - return (file1, file2) -> { - try (PDDocument doc1 = PDDocument.load(file1.getInputStream()); - PDDocument doc2 = PDDocument.load(file2.getInputStream())) { - String title1 = doc1.getDocumentInformation().getTitle(); - String title2 = doc2.getDocumentInformation().getTitle(); - return title1.compareTo(title2); - } catch (IOException e) { - return 0; - } - }; - case "orderProvided": - default: - return (file1, file2) -> 0; // Default is the order provided - } -} - -@PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs") -@Operation(summary = "Merge multiple PDF files into one", - description = "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO") -public ResponseEntity mergePdfs(@ModelAttribute MergePdfsRequest form) throws IOException { - - MultipartFile[] files = form.getFileInput(); - Arrays.sort(files, getSortComparator(form.getSortType())); - - List documents = new ArrayList<>(); - for (MultipartFile file : files) { - try (InputStream is = file.getInputStream()) { - documents.add(PDDocument.load(is)); - } - } - - try (PDDocument mergedDoc = mergeDocuments(documents)) { - ResponseEntity response = WebResponseUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf"); - return response; - } finally { + private PDDocument mergeDocuments(List documents) throws IOException { + PDDocument mergedDoc = new PDDocument(); for (PDDocument doc : documents) { - if (doc != null) { - doc.close(); + for (PDPage page : doc.getPages()) { + mergedDoc.addPage(page); } } + return mergedDoc; + } + + private Comparator getSortComparator(String sortType) { + switch (sortType) { + case "byFileName": + return Comparator.comparing(MultipartFile::getOriginalFilename); + case "byDateModified": + return (file1, file2) -> { + try { + BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class); + BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class); + return attr1.lastModifiedTime().compareTo(attr2.lastModifiedTime()); + } catch (IOException e) { + return 0; // If there's an error, treat them as equal + } + }; + case "byDateCreated": + return (file1, file2) -> { + try { + BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class); + BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class); + return attr1.creationTime().compareTo(attr2.creationTime()); + } catch (IOException e) { + return 0; // If there's an error, treat them as equal + } + }; + case "byPDFTitle": + return (file1, file2) -> { + try (PDDocument doc1 = PDDocument.load(file1.getInputStream()); + PDDocument doc2 = PDDocument.load(file2.getInputStream())) { + String title1 = doc1.getDocumentInformation().getTitle(); + String title2 = doc2.getDocumentInformation().getTitle(); + return title1.compareTo(title2); + } catch (IOException e) { + return 0; + } + }; + case "orderProvided": + default: + return (file1, file2) -> 0; // Default is the order provided + } + } + + @PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs") + @Operation(summary = "Merge multiple PDF files into one", + description = "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO") + public ResponseEntity mergePdfs(@ModelAttribute MergePdfsRequest form) throws IOException { + + try { + MultipartFile[] files = form.getFileInput(); + Arrays.sort(files, getSortComparator(form.getSortType())); + + PDFMergerUtility mergedDoc = new PDFMergerUtility(); + ByteArrayOutputStream docOutputstream = new ByteArrayOutputStream(); + + for (MultipartFile file : files) { + mergedDoc.addSource(new ByteArrayInputStream(file.getBytes())); + } + + mergedDoc.setDestinationFileName(files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "")); + mergedDoc.setDestinationStream(docOutputstream); + mergedDoc.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly()); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_PDF); + + // Here you have to set the actual filename of your pdf + headers.setContentDispositionFormData(mergedDoc.getDestinationFileName(), mergedDoc.getDestinationFileName()); + headers.setCacheControl("must-revalidate, post-check=0, pre-check=0"); + return new ResponseEntity<>(docOutputstream.toByteArray(), headers, HttpStatus.OK); + } catch (Exception ex) { + logger.error("Error in merge pdf process", ex); + throw ex; + } } } - -} \ No newline at end of file From cf3693186a2c3cdcf0325cdd762ef42aba2379df Mon Sep 17 00:00:00 2001 From: dkaitantzidis Date: Mon, 25 Dec 2023 23:27:08 +0200 Subject: [PATCH 2/2] Fixes headers issue in merge pdfs. --- .../SPDF/controller/api/MergeController.java | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java index cc890ee9..7db00a31 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -1,35 +1,32 @@ package stirling.software.SPDF.controller.api; -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; 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.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.MergePdfsRequest; import stirling.software.SPDF.utils.WebResponseUtils; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + @RestController @RequestMapping("/api/v1/general") @Tag(name = "General", description = "General APIs") @@ -93,7 +90,6 @@ public class MergeController { @Operation(summary = "Merge multiple PDF files into one", description = "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO") public ResponseEntity mergePdfs(@ModelAttribute MergePdfsRequest form) throws IOException { - try { MultipartFile[] files = form.getFileInput(); Arrays.sort(files, getSortComparator(form.getSortType())); @@ -105,17 +101,11 @@ public class MergeController { mergedDoc.addSource(new ByteArrayInputStream(file.getBytes())); } - mergedDoc.setDestinationFileName(files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "")); + mergedDoc.setDestinationFileName(files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf"); mergedDoc.setDestinationStream(docOutputstream); mergedDoc.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly()); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_PDF); - - // Here you have to set the actual filename of your pdf - headers.setContentDispositionFormData(mergedDoc.getDestinationFileName(), mergedDoc.getDestinationFileName()); - headers.setCacheControl("must-revalidate, post-check=0, pre-check=0"); - return new ResponseEntity<>(docOutputstream.toByteArray(), headers, HttpStatus.OK); + return WebResponseUtils.bytesToWebResponse(docOutputstream.toByteArray(), mergedDoc.getDestinationFileName()); } catch (Exception ex) { logger.error("Error in merge pdf process", ex); throw ex;