在 Web 应用程序 Java 中将许多 PDF 文件合并为一个 PDF 文件



>我有很多pdf文件,我必须将所有pdf合并到一个大的pdf文件中并将其渲染到浏览器中。我正在使用itext。使用它,我能够将pdf文件合并到一个文件中到磁盘中,但我无法合并到浏览器中,并且浏览器中只有最后一个pdf。以下是我的代码。请帮我解决这个问题。

提前谢谢。

Document document = new Document();
List<PdfReader> readers = 
new ArrayList<PdfReader>();
int totalPages = 0;
ServletOutputStream servletOutPutStream = response.getOutputStream();;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();;
InputStream is=null;
List<InputStream> inputPdfList = new ArrayList<InputStream>();
System.err.println(imageMap.size());
for(byte[] imageList:imageMap)
{
System.out.println(imageList.toString()+"   "+imageList.length);

byteArrayOutputStream.write(imageList);
byteArrayOutputStream.writeTo(response.getOutputStream());
is = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); 
inputPdfList.add(is);
}
response.setContentType("application/pdf");
response.setContentLength(byteArrayOutputStream.size());
System.out.println(inputPdfList.size()+""+inputPdfList.toString());
//Create pdf Iterator object using inputPdfList.
Iterator<InputStream> pdfIterator = 
inputPdfList.iterator();
// Create reader list for the input pdf files.
while (pdfIterator.hasNext()) {
InputStream pdf = pdfIterator.next();
PdfReader pdfReader = new PdfReader(pdf);
readers.add(pdfReader);
totalPages = totalPages + pdfReader.getNumberOfPages();
}
// Create writer for the outputStream
PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
//Open document.
document.open();
//Contain the pdf data.
PdfContentByte pageContentByte = writer.getDirectContent();
PdfImportedPage pdfImportedPage;
int currentPdfReaderPage = 1;
Iterator<PdfReader> iteratorPDFReader = readers.iterator();
// Iterate and process the reader list.
while (iteratorPDFReader.hasNext()) {
PdfReader pdfReader = iteratorPDFReader.next();
//Create page and add content.
while (currentPdfReaderPage <= pdfReader.getNumberOfPages()) {
document.newPage();
pdfImportedPage = writer.getImportedPage(
pdfReader,currentPdfReaderPage);
pageContentByte.addTemplate(pdfImportedPage, 0, 0);
currentPdfReaderPage++;
}
currentPdfReaderPage = 1;
}
//Close document and outputStream.
servletOutPutStream.flush();
outputStream.flush();
document.close();
outputStream.close();
servletOutPutStream.close();
System.out.println("Pdf files merged successfully.");

代码中存在许多错误:

仅将要返回给浏览器的内容写入响应输出流

您的代码将大量数据写入响应输出流:

ServletOutputStream servletOutPutStream = response.getOutputStream();;
[...]
for(byte[] imageList:imageMap)
{
[...]
byteArrayOutputStream.writeTo(response.getOutputStream());
[...]
}
[...]
PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
[... merge PDFs into the writer]
servletOutPutStream.flush();
document.close();
servletOutPutStream.close();

这会导致imageMap元素的许多副本写入其中,并且仅在此后添加合并的文件。

您希望浏览器做什么,忽略所有领先的源PDF副本,直到最终出现合并的PDF?

因此,请仅将合并的PDF写入响应输出流。

不要写错内容长度

最好将内容长度写入响应...但前提是您使用正确的值!

在代码中,您编写内容长度:

response.setContentLength(byteArrayOutputStream.size());

但此时的byteArrayOutputStream只包含源 PDF 的大量副本,而不包含最终合并的 PDF。因此,这只会使浏览器更加混乱。

因此,请不要在响应中添加错误的标头。

不要破坏输入数据

在循环中

for(byte[] imageList:imageMap)
{
System.out.println(imageList.toString()+"   "+imageList.length);
byteArrayOutputStream.write(imageList);
byteArrayOutputStream.writeTo(response.getOutputStream());
is = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); 
inputPdfList.add(is);
}

你取byte数组,我假设每个数组都包含一个源 PDF,用它们污染响应输出流(如前所述),并创建一个输入流集合,其中第一个包含第一个源 PDF,第二个包含前两个源PDF 的串联,第三个是前三个源 PDF 的串联, 等。。。

因为你从不重置或重新实例化byteArrayOutputStream,它只会越来越大。

因此,请像这样开始或结束循环,重置byteArrayOutputStream

(实际上你根本不需要那个循环,PdfReader有一个构造函数,可以立即接受byte[],不需要将其包装在字节流中。

不要使用纯PdfWriter合并 PDF,请使用PdfCopy

您可以使用PdfWriter/getImportedPage/addTemplate方法合并PDF。关于堆栈溢出有几十个问题和答案(其中许多由iText开发人员回答),解释说这通常是一个坏主意,您应该使用PdfCopy

因此,请利用这里已经存在的关于这个主题的许多好答案,并使用PdfCopy进行合并。

不要仅仅因为可以刷新或关闭流

您可以通过关闭大量流来完成响应输出:

//Close document and outputStream.
servletOutPutStream.flush();
outputStream.flush();
document.close();
outputStream.close();
servletOutPutStream.close();

我还没有看到您声明或设置该outputStream变量的行,但即使它包含响应输出流,也无需关闭它,因为您已经在servletOutPutStream变量中关闭了它。

因此,请删除不必要的此类调用。

//假设我们想将一个pdf与另一个主pdf合并

InputStream is1 = null;

if (file1 != null) {
FileInputStream fis1 = new FileInputStream(file1);
byte[] file1Data = new byte[(int) file1.length()];
fis1.read(file1Data);
is1 = new java.io.ByteArrayInputStream(file1Data);
}

//
InputStream mainContent = <ur main content>

org.apache.pdfbox.pdmodel.PDDocument mergedPDF = new org.apache.pdfbox.pdmodel.PDDocument();
org.apache.pdfbox.pdmodel.PDDocument mainDoc = org.apache.pdfbox.pdmodel.PDDocument.load(mainContent);
org.apache.pdfbox.multipdf.PDFMergerUtility merger = new org.apache.pdfbox.multipdf.PDFMergerUtility();

merger.appendDocument(mergedPDF, mainDoc);

PDDocument doc1 = null;

if (is1 != null) {
doc1 = PDDocument.load(is1);
merger.appendDocument(mergedPDF, doc1);
//1st file appended to main pdf");
}


ByteArrayOutputStream baos = new ByteArrayOutputStream();
mergedPDF.save(baos);

现在要么你把它保存在这里,要么转换成输入流,如果你愿意的话

ByteArrayInputStream mergedInputStream = new ByteArrayInputStream(baos.toByteArray());

最新更新