使用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,这种概率很低
给你几个针对性的修复建议:
- 严格把控子流的关闭时机:所有从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(); }
别让外部方法插手document的生命周期:如果需要把document或者它的子对象传给其他方法处理,一定要约定好:外部方法只做业务操作,绝对不能调用close方法,所有的资源关闭都统一放在最外层的finally块(或者用下面说的try-with-resources)。
改用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




