首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java8 FilterOutputStream异常

Java8 FilterOutputStream异常
EN

Stack Overflow用户
提问于 2014-08-07 06:49:06
回答 4查看 1.7K关注 0票数 4

这是对Java8中的FilterOutputStream.close()方法的更改,这给我们带来了一些问题。(见http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/759aa847dcaf)

在早期版本的Java中,以下代码工作正常,不会引发异常。然而,在Java 8中,当尝试资源机制关闭流时,我们总是会遇到异常。

代码语言:javascript
复制
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封装了包装outStreambosdeflaterStream.close()将调用bos.close(),后者将调用outStream.close(),后者将将底层流关闭到数据库。

下一个尝试资源机制将在bos上调用bos。由于bos扩展了FilterOutputStream,所以首先将在outStream上调用flush()。但是,由于outStream已经关闭,outStream.flush()抛出异常:java.sql.SQLException: Closed LOB

代码语言:javascript
复制
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

还有其他人经历过这个问题吗?如果是这样的话,你是如何解决的呢?我们使用资源的方式有什么问题吗?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-08-07 23:28:07

这是FilterOutputStream中的一个bug。这实现了可关闭。这一接口的合同规定:

关闭此流并释放与其关联的任何系统资源。如果流已经关闭,则调用此方法无效。

因此,第二次调用.close()没有任何效果,但在这种情况下,它调用了抛出异常的flush()

FilterOutputStream被卡在继承层次结构中,因此在问题发生时应用修复并不容易。

如果不能在try块中将某些流声明为局部变量,就会导致像FindBugs和Eclipse这样的工具将代码标记为资源使用。勤奋的开发人员在稍后查看这段代码时可能会考虑到同样的问题。

您可以创建一个类,CloseOnceBufferedOutputStream,它扩展了BufferedOutputStream。它将有一个boolean来记住,如果它已经关闭,以便它可以满足合同。重写close()方法以检查流是否已关闭。如果是这样的话,只要return

如果Oracle修复了底层错误,那么您的新类将是无用的,但是代码将继续工作。

票数 2
EN

Stack Overflow用户

发布于 2014-08-07 06:54:21

不要在bosoutStream中声明try,这样它们就不会自动关闭。只有声明的AutoClosable将被关闭。

JVM不分析声明的AutoClosable是否使用另一个构造,因此必须显式关闭其中的每个。如果他们的close()方法只能被调用一次,这可能会导致您正在经历的问题。

只需像这样声明deflaterStream

代码语言:javascript
复制
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 )返回的流,以防出现其他问题:

代码语言:javascript
复制
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){}
    }
}
票数 1
EN

Stack Overflow用户

发布于 2014-08-07 06:55:29

你不应该这么做但是..。

您不需要声明所创建的所有输出流,而只需声明最外层的输出流。

代码语言:javascript
复制
try( InputStream bis = new BufferedInputStream( inputStream );
     DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
         new BufferedOutputStream( payloadData.setBinaryStream( 0 ) ), new Deflater( 3 ) ) )
{
    fileSize = IOUtil.copy( bis, blobDeflaterStream );
}

这样,deflatorStream将被关闭,这将关闭其他的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25175882

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档