pdf合并期间的OutOfMemoryError

时间:2010-05-21 09:18:45

标签: java pdf merge itext

以下代码合并pdf文件并返回合并的pdf数据。当这段代码运行时,我尝试将100个文件与大约500kb的每个文件组合在一起,我在行document.close();中得到了outofmemory错误。这个代码在web环境中运行,是webspehere服务器可用的内存有问题吗?我在一篇文章中读到了使用freeReader方法,但我无法理解如何使用它我的方案。

protected ByteArrayOutputStream joinPDFs(List<InputStream> pdfStreams,
        boolean paginate) {

    Document document = new Document();

    ByteArrayOutputStream mergedPdfStream = new ByteArrayOutputStream();

    try {
        //List<InputStream> pdfs = pdfStreams;
        List<PdfReader> readers = new ArrayList<PdfReader>();
        int totalPages = 0;
        //Iterator<InputStream> iteratorPDFs = pdfs.iterator();
        Iterator<InputStream> iteratorPDFs = pdfStreams.iterator();

        // Create Readers for the pdfs.
        while (iteratorPDFs.hasNext()) {
            InputStream pdf = iteratorPDFs.next();
            if (pdf == null)
                continue;
            PdfReader pdfReader = new PdfReader(pdf);
            readers.add(pdfReader);
            totalPages += pdfReader.getNumberOfPages();
        }

        //clear this
        pdfStreams = null;

        //WeakReference ref = new WeakReference(pdfs);
        //ref.clear();

        // Create a writer for the outputstream
        PdfWriter writer = PdfWriter.getInstance(document, mergedPdfStream);
        writer.setFullCompression();

        document.open();
        BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA,
                BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
        PdfContentByte cb = writer.getDirectContent(); // Holds the PDF
        // data

        PdfImportedPage page;
        int currentPageNumber = 0;
        int pageOfCurrentReaderPDF = 0;
        Iterator<PdfReader> iteratorPDFReader = readers.iterator();

        // Loop through the PDF files and add to the output.
        while (iteratorPDFReader.hasNext()) {
            PdfReader pdfReader = iteratorPDFReader.next();

            // Create a new page in the target for each source page.
            while (pageOfCurrentReaderPDF < pdfReader.getNumberOfPages()) {
                pageOfCurrentReaderPDF++;
                document.setPageSize(pdfReader
                        .getPageSizeWithRotation(pageOfCurrentReaderPDF));
                document.newPage();
                // pageOfCurrentReaderPDF++;
                currentPageNumber++;
                page = writer.getImportedPage(pdfReader,
                        pageOfCurrentReaderPDF);
                cb.addTemplate(page, 0, 0);

                // Code for pagination.
                if (paginate) {
                    cb.beginText();
                    cb.setFontAndSize(bf, 9);
                    cb.showTextAligned(PdfContentByte.ALIGN_CENTER, ""
                            + currentPageNumber + " of " + totalPages, 520,
                            5, 0);
                    cb.endText();
                }
            }
            pageOfCurrentReaderPDF = 0;
            System.out.println("now the size is: "+pdfReader.getFileLength());
        }
        mergedPdfStream.flush();
        document.close();
        mergedPdfStream.close();
        return mergedPdfStream;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (document.isOpen())
            document.close();
        try {
            if (mergedPdfStream != null)
                mergedPdfStream.close();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
    return mergedPdfStream;
}

由于 V

4 个答案:

答案 0 :(得分:3)

此代码将所有PDF合并到内存(堆中)的数组中,因此,内存使用量将随着合并的文件数量线性增长。

我不知道freeReader方法,但也许您可以尝试将合并的PDF写入临时文件而不是字节数组? mergedPdfStream将是FileOutputStream而不是ByteArrayOutputStream。然后你回来,例如File对客户端代码的引用。

或者你可以增加Java可以使用的内存量(-Xmx JVM参数),但如果要合并的文件数量最终增加,你会发现自己遇到同样的问题。

答案 1 :(得分:1)

这不是执行文件操作的正确方法。您正在使用内存中的ArrayListArray合并文件。您应该使用文件IO和缓冲技术。

您是否希望最后显示最终合并文件?然后,您可以在完成所有合并后打开文件。

  • 不要像你所示的那样只使用内存缓冲。使用File Io缓冲(byte[]我的意思)
  • 阅读后关闭每个文件并附加。

Java在启动时分配的内存有限,因此一次合并大量文件会导致应用程序崩溃。您应该使用ThreadPool在单独的线程中尝试此合并操作,以便您的应用程序不会因此而受到限制。

感谢。

答案 2 :(得分:0)

100个文件* 500 kB约为50 MB。如果最大堆大小为64 MB,我很确定此代码在这种情况下不起作用。

答案 3 :(得分:0)

首先,为什么要将所有Iterator&lt;&gt;的代码混乱?样板代码? 你听说过for陈述吗? 即

for (PDfReader pdfReader: readers) { 
      // code for each single PDF reader in readers
}

第二:考虑在完成后立即关闭pdfReader。这将有希望刷新一些缓冲区并释放原始PDF占用的内存。