簡體   English   中英

使用PDFBox合並大型PDF文件時出錯-文件標記'%% EOF'丟失結尾

[英]Error Merging Large PDF Files with PDFBox - Missing end of file marker '%%EOF'

我已經使用InputStreams使用PDFBox成功實現了pdf合並解決方案。 但是,當我嘗試合並非常大的文檔時,出現以下錯誤:

Caused by: java.io.IOException: Missing root object specification in trailer.
at org.apache.pdfbox.pdfparser.COSParser.parseTrailerValuesDynamically(COSParser.java:2832) ~[pdfbox-2.0.11.jar:2.0.11]
at org.apache.pdfbox.pdfparser.PDFParser.initialParse(PDFParser.java:173) ~[pdfbox-2.0.11.jar:2.0.11]
at org.apache.pdfbox.pdfparser.PDFParser.parse(PDFParser.java:220) ~[pdfbox-2.0.11.jar:2.0.11]
at org.apache.pdfbox.pdmodel.PDDocument.load(PDDocument.java:1144) ~[pdfbox-2.0.11.jar:2.0.11]
at org.apache.pdfbox.pdmodel.PDDocument.load(PDDocument.java:1060) ~[pdfbox-2.0.11.jar:2.0.11]
at org.apache.pdfbox.multipdf.PDFMergerUtility.legacyMergeDocuments(PDFMergerUtility.java:379) ~[pdfbox-2.0.11.jar:2.0.11]
at org.apache.pdfbox.multipdf.PDFMergerUtility.mergeDocuments(PDFMergerUtility.java:280) ~[pdfbox-2.0.11.jar:2.0.11]

我認為,更重要的是在錯誤之前發生的這些語句:

FINE (pdfparser.COSParser) [] - Missing end of file marker '%%EOF'
FINE (pdfparser.COSParser) [] - Set missing offset 388 for object 2 0 R

在我看來,它在非常大的文件中找不到'%%EOF'標記。 現在我知道它確實存在,因為我可以查看源代碼(不幸的是我無法提供文件本身)。

在網上進行一些搜索后,我發現COSParser類上有一個setEOFLookupRange()方法。 我想知道查詢范圍是否太小,這就是為什么它找不到'%%EOF'標記的原因。 問題是...我的代碼中根本沒有使用COSParser對象。 我只使用PDFMergerUtility類。 PDFMergerUtility似乎在COSParser使用COSParser

所以我的問題是

  1. 我對EOFLookupRange假設正確嗎?
  2. 如果是這樣,如何設置我的代碼中僅包含PDFMergerUtility而不包含COSParser對象的范圍?

非常感謝您的寶貴時間!

用下面的代碼更新

 private boolean getCoolDocuments(final String slateId, final String filePathAndName)
            throws IOException {

        boolean status = false;
        InputStream pdfStream = null;
        HttpURLConnection connection = null;
        final PDFMergerUtility merger = new PDFMergerUtility();
        final ByteArrayOutputStream mergedPdfOutputStream = new ByteArrayOutputStream();

        try {

            final List<SlateDocument> parsedSlateDocuments = this.getSpecificDocumentsFromSlate(slateId);

            if (!parsedSlateDocuments.isEmpty()) {

                // iterate through each document, adding each pdf stream to the merger utility
                int numberOfDocuments = 0;
                for (final SlateDocument slateDocument : parsedSlateDocuments) {

                    final String url = this.getBaseURL() + "/slate/" + slateId + "/documents/"
                            + slateDocument.getDocumentId();

                     /* code for RequestResponseUtil.initializeRequest(...) below */
                    connection = RequestResponseUtil.initializeRequest(url, "GET", this.getAuthenticationHeader(),
                            true, MediaType.APPLICATION_PDF_VALUE);

                    if (RequestResponseUtil.isSuccessful(connection.getResponseCode())) {
                        pdfStream = connection.getInputStream();

                    }
                    else {
                        /* do various things */
                    }

                    merger.addSource(pdfStream);
                    numberOfDocuments++;
                }

                merger.setDestinationStream(mergedPdfOutputStream);

                // merge the all the pdf streams together
               merger.mergeDocuments(MemoryUsageSetting.setupTempFileOnly());

               status = true;
            }
            else {
                LOG.severe("An error occurred while parsing the slated documents; no documents remain after parsing!");
            }
        }
        finally {
            RequestResponseUtil.close(pdfStream);

            this.disconnect(connection);
        }

        return status;
    }

   public static HttpURLConnection initializeRequest(final String url, final String method,
            final String httpAuthHeader, final boolean multiPartFormData, final String reponseType) {

    HttpURLConnection conn = null;

    try {
        conn = (HttpURLConnection) new URL(url).openConnection();
        conn.setRequestMethod(method);
        conn.setRequestProperty("X-Slater-Authentication", httpAuthHeader);
        conn.setRequestProperty("Accept", reponseType);
        if (multiPartFormData) {
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=BOUNDARY");
            conn.setDoOutput(true);
        }
        else {
            conn.setRequestProperty("Content-Type", "application/xml");
        }
    }
    catch (final MalformedURLException e) {
        throw new CustomException(e);
    }
    catch (final IOException e) {
        throw new CustomException(e);
    }
    return conn;

}

我懷疑這是InputStream的問題。 這並不是我真正想的,但基本上我是在(非常錯誤)的假設下做出這樣的假設:

           pdfStream = connection.getInputStream();
                /* ... */
           merger.addSource(pdfStream);

當然,這將無法正常工作,因為可能會讀取或可能不會讀取整個InputStream 需要顯式讀取它,直到到達最后一個-1字節為止。 我很確定在較小的文件上它可以正常工作,並且實際上可以在整個流中讀取,但是在較大的文件上,它根本沒有達到目的...因此找不到%%EOF標記。

解決方案是使用中間的ByteArrayOutputStream ,然后通過ByteArrayInputStream將其轉換回InputStream

因此,如果您替換以下代碼行:

pdfStream = connection.getInputStream();

上面的代碼:

                final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

                int c;
                while ((c = connection.getInputStream().read()) != -1) {
                    byteArrayOutputStream.write(c);
                }

                pdfStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());

您將得到一個可行的示例。

我可能最終會將其更改為實現,以改為使用Pipes或Circular Buffers ,但是至少目前為止這是可行的。

盡管這不一定是Java 101錯誤,但它更像是Java 102錯誤,仍然很可恥。 :/希望它會幫助別人。

感謝@Tilman Hausherr和@Master_ex提供的所有幫助!

我看了一下代碼,發現EOFLookupRange中的默認COSParser2048字節

我認為您的假設是正確的。

展望PDFParser延伸的COSParser ,是由內部使用的解析器PDFMergerUtility我看到它,可以設置其他EOFLookupRange通過使用系統屬性 系統屬性名稱是org.apache.pdfbox.pdfparser.nonSequentialPDFParser.eofLookupRange ,它應該是有效的整數。

是一個演示如何設置系統屬性的問題。

我沒有測試以上內容,但我希望它能起作用:)

PDFBox代碼的鏈接使用的是2.0.11版本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM