首先,这个异常大部分原因是因为关闭流的顺序导致的,下面看一下官方API的描述
void close()
throws IOException
Closes this stream and releases any system resources associated with it. If the stream is already closed then invoking this method has no effect.
Throws:
IOException - if an I/O error occurs
很关键的一点就是:关闭该流并释放与之关联的所有资源。
所以,一般情况下,关闭流都讲究:先打开的后关闭,后打开的先关闭。例如处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b。
另外,在关闭流的写法上,一般建议加上空判断
if(a!=null) a.close;
if(b!=null) b.close;
然而,我还是遇到了一个神奇的场景。
public static void splitExistFile() {
// 文档路径
String path = "D:\\Personality\\pdf\\";
// 文件名
String pdfFileName = "test.pdf";
// 每个文件最大页数
int filePageSize = 2;
// 待拆分文件的总页数
int totalPage;
// 拆分后的文件数量
int splitFileNum;
int pageIndex = 1;
PdfReader reader = null;
try {
String orignName = pdfFileName.split("\\.")[0];
reader = new PdfReader(path + pdfFileName);
totalPage = reader.getNumberOfPages();
splitFileNum = totalPage % filePageSize == 0 ? totalPage / filePageSize : totalPage / filePageSize + 1;
for (int i = 0; i < splitFileNum; i++) {
String newFileName = path + orignName + "_" + (i + 1) + ".pdf";
// 新建一个PDF文件
Document document = null;
PdfWriter writer = null;
try {
document = new Document();
writer = PdfWriter.getInstance(document, new FileOutputStream(newFileName));
document.open();
PdfContentByte pdfContentByte = writer.getDirectContent();
for (int j = 0; j < filePageSize; j++) {
document.newPage();
pdfContentByte.addTemplate(writer.getImportedPage(reader, pageIndex), 0, 0);
pageIndex ++;
if (pageIndex > totalPage)
break;
}
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (writer != null)
writer.close();
}catch (Exception e) {
e.printStackTrace();
}
if (document != null)
document.close();
}
}
} catch (IOException e1) {
e1.printStackTrace();
} finally {
if(reader!=null) reader.close();
}
}
上面代码大概的逻辑就是将一个pdf文件,根据需求拆分成多个pdf文件,在关闭writer时,按照上述写法就会报错,必须先关闭document,在关闭writer。具体的原因算不上特别清楚,虽然看过了源码。document关闭的其实是文件的监听状态,writer关闭的是io流,writer创建在document之后,甚至说对document对象有依赖关系,但是如果先关闭此对象,在循环内部有一个输出流刷新的动作,然后再关闭,第二次执行时,刷新动作就会抛出异常,因为流已经在第一次循环后关闭掉了。这方面有研究的朋友欢迎交流交流~