关闭流时,抛出异常:java.io.IOException: Stream Closed

本文探讨了在使用Java进行PDF文件拆分时遇到的流关闭顺序问题,详细解析了正确的流关闭顺序对避免IOException的重要性,特别是针对PdfWriter和Document的关闭顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先,这个异常大部分原因是因为关闭流的顺序导致的,下面看一下官方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对象有依赖关系,但是如果先关闭此对象,在循环内部有一个输出流刷新的动作,然后再关闭,第二次执行时,刷新动作就会抛出异常,因为流已经在第一次循环后关闭掉了。这方面有研究的朋友欢迎交流交流~