java - 将 itext 替换为 pdfbox 性能
问题描述
我正在评估将我们的 pdf 处理从 itext 替换为 pdfbox。我对单页(94KB、469KB、937KB)的 200 个 pdf 进行了一些测试,并将它们合并到我们的应用程序中的一个 pdf 中。PDFBox 版本:2.0.23。文本版本:2.1.7。以下是测试结果:
这是itext的实现:
byte[] l_PDFPage = null;
PdfReader l_PDFReader = null;
PdfCopy l_Copier = null;
Document l_PDFDocument = null;
OutputStream l_Stream = new FileOutputStream(m_File);
// do it for all pages in the editor
for( int i = 0; i < m_Editor.getCountOfElements(); i++ ) {
l_Page = m_Editor.getPageAt(i);
l_PDFPage = l_Page.getAsPdf();
l_PDFReader = new PdfReader(l_PDFPage);
l_PDFReader.getPageN(1).put(PdfName.ROTATE, new PdfNumber(l_PDFReader.getPageRotation(1) + l_Page.getRotation() % 360));
l_PDFReader.consolidateNamedDestinations();
if( i == 0 ) {
l_PDFDocument = new Document(l_PDFReader.getPageSizeWithRotation(1));
l_Copier = new PdfCopy(l_PDFDocument, l_Stream);
l_PDFDocument.open();
}
l_Copier.addPage(l_Copier.getImportedPage(l_PDFReader, 1));
if( l_PDFReader.getAcroForm() != null )
l_Copier.copyAcroForm(l_PDFReader);
l_Copier.flush();
l_Copier.freeReader(l_PDFReader);
}
l_PDFDocument.close();
l_Stream.close();
这是pdfbox的实现:
byte[] l_PDFPage = null;
List<PDDocument> pageDocuments = new ArrayList<>();
PDDocument saveDocument = new PDDocument();
try {
// do it for all pages in the editor
for( int i = 0; i < m_Editor.getCountOfElements(); i++ ) {
// our wrapper object for a page
l_Page = m_Editor.getPageAt(i);
// page as byte[]
l_PDFPage = l_Page.getAsPdf();
PDDocument document = PDDocument.load(l_PDFPage);
// save page document to close it later
pageDocuments.add(document);
PDPage page = document.getPage(0);
saveDocument.addPage(saveDocument.importPage(page));
}
saveDocument.save(l_Stream);
}
finally {
// close every page document
for(PDDocument doc : pageDocuments) {
doc.close();
}
saveDocument.close();
}
我也尝试过使用 pdfbox 的 pdfmerger。性能几乎与其他 pdfbox 实现相同。但是对于 937KB 的文件,我使用这个实现在内存不足的异常中运行:
byte[] l_PDFPage = null;
OutputStream l_Stream = new FileOutputStream(m_File);
PDFMergerUtility merger = new PDFMergerUtility();
// do it for all pages in the editor
for( int i = 0; i < m_Editor.getCountOfElements(); i++ ) {
l_Page = m_Editor.getPageAt(i);
// page as byte[]
l_PDFPage = l_Page.getAsPdf();
merger.addSource(new ByteArrayInputStream(l_PDFPage));
}
merger.setDestinationStream(l_Stream);
merger.mergeDocuments(null);
所以我的问题:
- 与 itext 相比,为什么 pdfbox 的性能(所需时间和内存使用)如此糟糕?
- 我在我们的 pdfbox 实现中遗漏了什么吗?
- 为什么我在“saveDocument”中添加页面后无法关闭“页面文档”?如果我在那里关闭它,我会在保存时出错,所以我必须存储“页面文档”并在最后关闭它们。
解决方案
PDFBox 和 iText 在架构上是不同的,因此在不同的任务中表现不同。
特别是 iText 尝试尽早写出新内容,在您的情况下,大部分页面已在期间写入输出
l_Copier.addPage(l_Copier.getImportedPage(l_PDFReader, 1));
和
l_PDFDocument.close();
最终只完成 PDF 并写入最后剩余的对象和文件预告片。
另一方面,PDFBox 一次保存所有内容:
saveDocument.save(l_Stream);
iText 的方法的优点是内存占用更小(如您所见),缺点是一旦写入页面数据就无法更改。
(顺便说一句:iText 架构已从 iText 5 更改为 iText 7,在 iText 7 中,您可以选择并且可以将所有内容保存在内存中,但这里的代价也是很大的内存占用。)
因此,
与 itext 相比,为什么 pdfbox 的性能(所需时间和内存使用)如此糟糕?
以上可以部分解释内存使用的差异。同样在 iText 之后
l_Copier.freeReader(l_PDFReader);
可以PdfReader
关闭(您留给垃圾收集器为您做的)以释放其资源,而在您的 PDFBox 代码中,您保持所有源文档打开,将资源保留到最后。(实际上我会假设当您使用 时importPage
,您不需要保留它们。)
关于时间我现在不确定。您应该做一些更精细的计时,并确定在 PDFBox 中究竟在哪里使用了额外的时间;因此,我支持@Tilman 的分析数据请求。我认为这是在最后一次保存期间,但这只是一种预感。此外,此类时间差异可能取决于相关 PDF 的结构细节,并且对于其他文档可能不那么极端。
推荐阅读
- php - HelloSign iframe 在 PHP 自定义页面中显示
- javascript - C# 方法跳过 java 脚本调用
- csv - Grails:将解析的 csv 数据保存到数据库
- c++ - 前向声明 typedef 保护
- intellij-idea - 协程工作但 IntelliJ 显示错误
- javascript - 蜘蛛/雷达图 D3.js 的轴问题
- javascript - 错误:未捕获(在承诺中):未处理的承诺拒绝
- ionic-framework - 项目中每个页面的 Ionic 3 选项卡
- java - 使用 ajax 将模型和字符串传递给 Spring MVC
- javascript - JavaScript 中的对象类型