[英]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
。
所以我的問題是
EOFLookupRange
假設正確嗎? 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
中的默認COSParser
為2048
字節 。
我認為您的假設是正確的。
展望PDFParser
延伸的COSParser
,是由內部使用的解析器PDFMergerUtility
我看到它,可以設置其他EOFLookupRange
通過使用系統屬性 。 系統屬性名稱是org.apache.pdfbox.pdfparser.nonSequentialPDFParser.eofLookupRange
,它應該是有效的整數。
這是一個演示如何設置系統屬性的問題。
我沒有測試以上內容,但我希望它能起作用:)
PDFBox代碼的鏈接使用的是2.0.11版本。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.