OutOfMemoryError во время слияния PDF - PullRequest
1 голос
/ 21 мая 2010

приведенный ниже код объединяет PDF-файлы и возвращает объединенные PDF-данные. в то время как этот код выполняется, я пытаюсь объединить 100 файлов с каждым файлом приблизительно в 500 КБ, я получаю ошибку outofmemory в строке document.close () ;. этот код работает в веб-среде, является ли память доступной для веб-сервера здесь проблема? Я прочитал в статье, чтобы использовать метод 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;
}

Спасибо В

Ответы [ 4 ]

3 голосов
/ 21 мая 2010

Этот код объединяет все PDF-файлы в массиве в памяти (куче), поэтому да, использование памяти будет расти линейно с увеличением количества объединяемых файлов.

Я не знаю о методе freeReader, но, возможно, вы могли бы попробовать записать объединенный PDF во временный файл вместо байтового массива? mergedPdfStream будет FileOutputStream вместо ByteArrayOutputStream. Затем вы вернетесь, например. File ссылка на код клиента.

Или вы можете увеличить количество памяти, которое может использовать Java (-Xmx параметр JVM), но если количество объединяемых файлов со временем увеличится, вы столкнетесь с той же проблемой.

1 голос
/ 21 мая 2010

Это неправильный способ выполнения файловой операции. Вы делаете слияние файлов, используя ArrayList и Array в памяти. Лучше использовать File IO с методами буферизации.

Вы хотите показать окончательный объединенный файл, наконец? Затем вы можете открыть файл после того, как все слияние будет завершено.

  • Не используйте только буферизацию в памяти, как вы показали. Использовать File Io с буферизацией (byte[] я имею в виду)
  • Закройте каждый файл после прочтения и добавьте его.

Java имеет ограниченную память, выделенную во время запуска, поэтому одновременное объединение большого количества файлов приведет к сбою приложения Вы должны попробовать эту операцию слияния в отдельном потоке, используя ThreadPool, чтобы ваше приложение не застряло для этого.

спасибо.

0 голосов
/ 21 мая 2010

Во-первых, почему вы загромождаете свой код всем этим стандартным кодом Iterator <>? Вы когда-нибудь слышали о for заявлении? то есть

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

Второе: подумайте о том, чтобы закрыть pdfReader, как только это будет сделано. Мы надеемся, что это очистит некоторые буферы и освободит память, занятую исходным PDF.

0 голосов
/ 21 мая 2010

100 файлов * 500 кБ это что-то около 50 МБ. Если максимальный размер кучи составляет 64 МБ, я уверен, что этот код не будет работать в таких условиях.

...