
在使用itext合并多个pdf文档时,常见的做法是将每个源pdf的内容逐一添加到pdfcopy或pdfsmartcopy对象中,并将最终输出定向到一个bytearrayoutputstream,以便之后将其转换为字节数组或发送到网络。例如,以下代码片段展示了这种常见的实现方式:
public static byte[] mergePdf(List < InputStream > inputStreams) {
Document document = new Document();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 内存占用源
PdfCopy copy = new PdfSmartCopy(document, byteArrayOutputStream);
document.open();
for (InputStream inputStream: inputStreams) {
PdfReader pdfReader = new PdfReader(inputStream);
copy.addDocument(pdfReader);
copy.freeReader(pdfReader); // 释放PdfReader的内部缓存
pdfReader.close();
}
document.close();
return byteArrayOutputStream.toByteArray(); // 在此处将整个PDF转换为字节数组
}尽管PdfReader在处理后通过freeReader和close方法释放了内部资源,但ByteArrayOutputStream却会持续累积所有合并后的PDF数据。当合并的PDF数量较多或单个PDF文件较大时,ByteArrayOutputStream可能会增长到超出JVM堆内存限制,从而抛出OutOfMemoryError: Java Heap Space。这种方法本质上是将整个合并后的PDF文件完全加载到内存中,这与避免创建临时文件的初衷相悖,因为内存本身就是一种“临时存储”。
解决此问题的关键在于避免使用中间的ByteArrayOutputStream。如果最终目的是将合并后的PDF发送到某个OutputStream(例如,HTTP响应流、文件输出流等),那么最直接且内存效率最高的方法是直接将PdfCopy的输出流连接到这个目标OutputStream。这样,iText在合并PDF时,数据会直接写入目标流,而不会在内存中进行完整的缓存。
核心思想: 将PdfCopy的构造函数中的ByteArrayOutputStream替换为目标OutputStream。
以下是优化后的mergePdf方法,它接受一个OutputStream作为参数,将合并后的PDF直接写入该流:
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSmartCopy;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.List;
public class PdfMergeUtil {
/**
* 将多个PDF输入流合并到一个输出流中,避免内存溢出。
*
* @param inputStreams 待合并的PDF输入流列表
* @param outputStream 目标输出流,合并后的PDF将直接写入此流
* @throws IOException 如果在PDF处理过程中发生I/O错误
*/
public static void mergePdfsToStream(List<InputStream> inputStreams, OutputStream outputStream) throws IOException {
Document document = null;
PdfCopy copy = null;
try {
document = new Document();
// 直接将PdfCopy连接到目标OutputStream
copy = new PdfSmartCopy(document, outputStream);
document.open();
for (InputStream inputStream : inputStreams) {
PdfReader pdfReader = null;
try {
pdfReader = new PdfReader(inputStream);
copy.addDocument(pdfReader);
copy.freeReader(pdfReader); // 释放PdfReader的内部缓存
} finally {
// 确保PdfReader和其底层的InputStream被关闭
if (pdfReader != null) {
pdfReader.close();
}
if (inputStream != null) {
try {
inputStream.close(); // 关闭原始输入流
} catch (IOException e) {
// 记录或处理关闭异常
System.err.println("Error closing input stream: " + e.getMessage());
}
}
}
}
} finally {
// 确保Document和OutputStream被关闭
if (document != null && document.isOpen()) {
document.close();
}
// 注意:此处不关闭outputStream,因为调用者可能需要继续使用或由其负责关闭
// 如果outputStream是方法内部创建的,则应在此处关闭
}
}
// 示例用法:将合并后的PDF写入文件
/*
public static void main(String[] args) {
List<InputStream> pdfInputStreams = new ArrayList<>();
try {
// 假设你有多个PDF文件,转换为InputStream
pdfInputStreams.add(new FileInputStream("path/to/pdf1.pdf"));
pdfInputStreams.add(new FileInputStream("path/to/pdf2.pdf"));
// ...
FileOutputStream fos = new FileOutputStream("merged_output.pdf");
mergePdfsToStream(pdfInputStreams, fos);
System.out.println("PDFs merged successfully to merged_output.pdf");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 确保所有输入流和输出流都被关闭
for (InputStream is : pdfInputStreams) {
try {
if (is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// fos在try-with-resources或单独的finally块中关闭
}
}
*/
}通过将iText的PdfCopy直接连接到目标OutputStream,我们能够实现真正的流式PDF合并,避免在内存中缓存整个合并后的文件。这种方法不仅解决了OutOfMemoryError问题,还提高了应用程序的健壮性和可伸缩性,尤其适用于处理大量或大型PDF文件的场景,如在线文档生成、文件下载服务等。这种内存效率高的处理方式是构建高性能、稳定PDF处理服务的关键。
以上就是iText PDF合并优化:避免内存溢出的高效流式处理的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号