You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Apache PDFBox保存PDF遇COSStream已关闭错误,求排查原因

解决Apache PDFBox保存PDF时的COSStream has been closed错误

你遇到的这个错误提示其实已经把问题点出来了:

java.io.IOException: COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed?

简单说就是你要读取的COSStream已经被关闭了,要么是它所属的PDDocument被提前关闭,要么是这个流本身在之前的操作中被误关了。我之前用PDFBox做批量PDF处理时也踩过这个坑,结合你的代码结构来看,问题大概率出在try块里的...部分,常见的情况有这几种:

  • 你在操作过程中不小心提前调用了document.close(),或者关闭了某个从document衍生出来的流对象(比如PDPageContentStream),但后续的save操作还需要访问这些流资源
  • 你把document或者它的子对象(比如PDPage)传给了外部方法,而那个方法里偷偷关闭了相关资源
  • 极少数情况是save之后还有代码在访问document内容,但你的代码里save之后直接到finally的close,这种概率很低

给你几个针对性的修复建议:

  1. 严格把控子流的关闭时机:所有从document创建的子流(比如用来编辑页面内容的PDPageContentStream),必须在document.save()之前完成关闭,绝对不能在save之后才关,更不能在save之前就把document给关了。示例代码:
PDDocument document = PDDocument.load(new File(fileName));
try {
    PDPage targetPage = document.getPage(0);
    // 创建内容编辑流
    PDPageContentStream contentStream = new PDPageContentStream(document, targetPage);
    // 这里做你的内容修改操作,比如添加文字、图片
    contentStream.beginText();
    contentStream.setFont(PDType1Font.HELVETICA, 12);
    contentStream.newLineAtOffset(100, 700);
    contentStream.showText("Hello PDFBox");
    contentStream.endText();
    // 必须在save前关闭这个子流!
    contentStream.close();
    
    // 现在执行保存
    document.save(storeFileName);
} finally {
    document.close();
}
  1. 别让外部方法插手document的生命周期:如果需要把document或者它的子对象传给其他方法处理,一定要约定好:外部方法只做业务操作,绝对不能调用close方法,所有的资源关闭都统一放在最外层的finally块(或者用下面说的try-with-resources)。

  2. 改用try-with-resources语法更省心:PDFBox的PDDocument实现了AutoCloseable接口,用try-with-resources可以自动帮你管理资源,完全不用手动写finally和close,能从根源上避免提前关闭或者漏关的问题:

// 用try-with-resources包裹document,代码块结束自动关闭
try (PDDocument document = PDDocument.load(new File(fileName))) {
    // 这里执行所有操作:修改内容、添加页面等等
    PDPage newPage = new PDPage();
    document.addPage(newPage);
    
    // 保存文件
    document.save(storeFileName);
}
// 这里不需要写close,JVM会自动处理

内容的提问来源于stack exchange,提问作者Sergey Sukhotskiy

火山引擎 最新活动