这是对Java8中的FilterOutputStream.close()方法的更改,这给我们带来了一些问题。(见http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/759aa847dcaf)
在早期版本的Java中,以下代码工作正常,不会引发异常。然而,在Java 8中,当尝试资源机制关闭流时,我们总是会遇到异常。
try( InputStream bis = new BufferedInputStream( inputStream );
OutputStream outStream = payloadData.setBinaryStream( 0 );
BufferedOutputStream bos = new BufferedOutputStream( outStream );
DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
bos, new Deflater( 3 ) ) )
{
fileSize = IOUtil.copy( bis, deflaterStream );
}使用资源的尝试机制将首先在deflaterStream上调用deflaterStream。由于deflaterStream封装了包装outStream的bos,deflaterStream.close()将调用bos.close(),后者将调用outStream.close(),后者将将底层流关闭到数据库。
下一个尝试资源机制将在bos上调用bos。由于bos扩展了FilterOutputStream,所以首先将在outStream上调用flush()。但是,由于outStream已经关闭,outStream.flush()抛出异常:java.sql.SQLException: Closed LOB
caused by: java.io.IOException: Closed LOB
at oracle.jdbc.driver.OracleBlobOutputStream.ensureOpen(OracleBlobOutputStream.java:265)
at oracle.jdbc.driver.OracleBlobOutputStream.flush(OracleBlobOutputStream.java:167)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:141)
at java.io.FilterOutputStream.close(FilterOutputStream.java:158)
at com.blah.uploadFile(CustomerUploadFacade.java:162)
... 38 more
Caused by: java.sql.SQLException: Closed LOB
at oracle.jdbc.driver.OracleBlobOutputStream.ensureOpen(OracleBlobOutputStream.java:257)
... 42 more还有其他人经历过这个问题吗?如果是这样的话,你是如何解决的呢?我们使用资源的方式有什么问题吗?
发布于 2014-08-07 23:28:07
这是FilterOutputStream中的一个bug。这实现了可关闭。这一接口的合同规定:
关闭此流并释放与其关联的任何系统资源。如果流已经关闭,则调用此方法无效。
因此,第二次调用.close()没有任何效果,但在这种情况下,它调用了抛出异常的flush()。
FilterOutputStream被卡在继承层次结构中,因此在问题发生时应用修复并不容易。
如果不能在try块中将某些流声明为局部变量,就会导致像FindBugs和Eclipse这样的工具将代码标记为资源使用。勤奋的开发人员在稍后查看这段代码时可能会考虑到同样的问题。
您可以创建一个类,CloseOnceBufferedOutputStream,它扩展了BufferedOutputStream。它将有一个boolean来记住,如果它已经关闭,以便它可以满足合同。重写close()方法以检查流是否已关闭。如果是这样的话,只要return。
如果Oracle修复了底层错误,那么您的新类将是无用的,但是代码将继续工作。
发布于 2014-08-07 06:54:21
不要在bos和outStream中声明try,这样它们就不会自动关闭。只有声明的AutoClosable将被关闭。
JVM不分析声明的AutoClosable是否使用另一个构造,因此必须显式关闭其中的每个。如果他们的close()方法只能被调用一次,这可能会导致您正在经历的问题。
只需像这样声明deflaterStream:
try( InputStream bis = new BufferedInputStream( inputStream );
DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
new BufferedOutputStream( payloadData.setBinaryStream( 0 ) ),
new Deflater( 3 ) ) )
{
fileSize = IOUtil.copy( bis, deflaterStream );
}编辑:
在下面的注释中,您可以关闭payloadData.setBinaryStream( 0 )返回的流,以防出现其他问题:
OutputStream outStream = null;
try( InputStream bis = new BufferedInputStream( inputStream );
DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
new BufferedOutputStream( outStream = payloadData.setBinaryStream( 0 ) ),
new Deflater( 3 ) ) )
{
fileSize = IOUtil.copy( bis, deflaterStream );
}
catch (ExceptionsYouWantToCatch eywtc)
{
if (outStream != null) {
// Here you have the chance to close it
try { outStream.close(); } catch(IOException ie){}
}
}发布于 2014-08-07 06:55:29
你不应该这么做但是..。
您不需要声明所创建的所有输出流,而只需声明最外层的输出流。
try( InputStream bis = new BufferedInputStream( inputStream );
DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
new BufferedOutputStream( payloadData.setBinaryStream( 0 ) ), new Deflater( 3 ) ) )
{
fileSize = IOUtil.copy( bis, blobDeflaterStream );
}这样,deflatorStream将被关闭,这将关闭其他的。
https://stackoverflow.com/questions/25175882
复制相似问题