如何检查 ImageInputStream 是否已经关闭?
How to check that ImageInputStream has been closed already?
在我目前的项目中,我使用了处理图像的第三方库。在某些情况下,我不知道 ImageInputStream
是从哪里来的(库的源代码是专有的,我无法编辑该代码)。但是我需要关闭每个流以释放资源,无论它们的来源如何。
javax.imageio.stream.ImageInputStream#close method throws exception
when the stream has been closed already.
我知道 ((MemoryCacheImageInputStream) ios).isClosed()
把戏。但是该方法具有私有访问级别并强制执行令人讨厌的转换。
我也知道另一种方法:捕获 IOException
,检查消息并抑制异常(当它与关闭相关时)或重新抛出它(否则),就像这样:
try {
imageInputStream.close();
} catch (IOException onClose) {
String message = onClose.getMessage();
if ("closed".equals(message)) {
// suppress the exception and write to log
} else {
throw new IllegalStateException(onClose);
}
}
有没有一种优雅的方法来检查 ImageInputStream
的状态?
一种方法是创建一个扩展 ImageInputStream
的 class 并实现您自己的 isClosed()
方法,例如通过覆盖 close()
方法以关闭时将布尔标志设置为 true。
你真的不需要检查流的状态,你只需要确保它没有关闭超过一次。一种选择是将 ImageInputStream
包装在另一个 class 中,在流已关闭的情况下覆盖 close()
成为空操作。这样做的好处是,它可以很好地与 try-with-resources 一起使用,例如:
try (ImageInputStream stream = new CloseableStreamFix(ImageIO.createImageInputStream(input))) {
stream.close(); // Close stream once (or as many times you want)
}
// stream implicitly closed again by automatic resource handling, no exception
不幸的是,CloseableStreamFix
的代码很重要,所以我不确定它是否算作 "elegant"(虽然是用法):
final class CloseableStreamFix extends ImageInputStreamImpl {
private boolean closed;
private final ImageInputStream delegate;
public CloseableStreamFix(ImageInputStream delegate) {
this.delegate = delegate;
}
// The method you actually want to override.
@Override
public void close() throws IOException {
if (!closed) {
closed = true;
super.close();
delegate.close();
}
}
// You have to implement these abstract read methods. Easy, just delegate them.
// ...except you need to keep the stream position in sync.
@Override
public int read() throws IOException {
streamPos++;
return delegate.read();
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int read = delegate.read(b, off, len);
if (read > 0) {
streamPos += read;
}
return read;
}
// In a perfect world, the above should be all you need to do. Unfortunately, it's not.
// We need to keep the delegate position in sync with the position in this class.
// Overriding the seek method should do.
@Override
public void seek(long pos) throws IOException {
super.seek(pos); // Don't forget to call super here, as we rely on positions being in sync.
delegate.seek(pos);
}
// Some plugins require stream length, so we need to delegate that.
@Override
public long length() {
try {
// Unfortunately, this method does not declare IOException like the
// interface method does, so we need this strange try/catch here.
return delegate.length();
} catch (IOException e) {
// It's also possible to use a generics hack to throw a checked
// exception as unchecked. I leave that as an exercise...
throw new UndeclaredThrowableException(e);
}
}
// You may be able to skip the flush methods. If you do, skip both.
@Override
public void flushBefore(long pos) throws IOException {
delegate.flushBefore(pos);
}
@Override
public long getFlushedPosition() {
return delegate.getFlushedPosition();
}
// You could probably skip the methods below, as I don't think they are ever used as intended.
@Override
public boolean isCached() {
return delegate.isCached();
}
@Override
public boolean isCachedMemory() {
return delegate.isCachedMemory();
}
@Override
public boolean isCachedFile() {
return delegate.isCachedFile();
}
}
...虽然我认为以上内容涵盖了所有基础,但您可能应该对其进行测试。
除非您打算使用大量 try-with-resources 语句,否则您可能会发现一个简单的 try/catch(就像您已经拥有的那样)更具可读性。不过,我会将其提取为这样的方法:
static void close(Closeable closeable) throws IOException {
try {
closeable.close();
}
catch (IOException e) {
if (!"closed".equals(e.getMessage())) {
throw e;
}
// Otherwise, we're already closed, just ignore it,
}
}
请注意,依赖这样的异常消息 可能 在未来的 Java 版本中中断,如果有人决定需要更好的解释...
在我目前的项目中,我使用了处理图像的第三方库。在某些情况下,我不知道 ImageInputStream
是从哪里来的(库的源代码是专有的,我无法编辑该代码)。但是我需要关闭每个流以释放资源,无论它们的来源如何。
javax.imageio.stream.ImageInputStream#close method throws exception when the stream has been closed already.
我知道 ((MemoryCacheImageInputStream) ios).isClosed()
把戏。但是该方法具有私有访问级别并强制执行令人讨厌的转换。
我也知道另一种方法:捕获 IOException
,检查消息并抑制异常(当它与关闭相关时)或重新抛出它(否则),就像这样:
try {
imageInputStream.close();
} catch (IOException onClose) {
String message = onClose.getMessage();
if ("closed".equals(message)) {
// suppress the exception and write to log
} else {
throw new IllegalStateException(onClose);
}
}
有没有一种优雅的方法来检查 ImageInputStream
的状态?
一种方法是创建一个扩展 ImageInputStream
的 class 并实现您自己的 isClosed()
方法,例如通过覆盖 close()
方法以关闭时将布尔标志设置为 true。
你真的不需要检查流的状态,你只需要确保它没有关闭超过一次。一种选择是将 ImageInputStream
包装在另一个 class 中,在流已关闭的情况下覆盖 close()
成为空操作。这样做的好处是,它可以很好地与 try-with-resources 一起使用,例如:
try (ImageInputStream stream = new CloseableStreamFix(ImageIO.createImageInputStream(input))) {
stream.close(); // Close stream once (or as many times you want)
}
// stream implicitly closed again by automatic resource handling, no exception
不幸的是,CloseableStreamFix
的代码很重要,所以我不确定它是否算作 "elegant"(虽然是用法):
final class CloseableStreamFix extends ImageInputStreamImpl {
private boolean closed;
private final ImageInputStream delegate;
public CloseableStreamFix(ImageInputStream delegate) {
this.delegate = delegate;
}
// The method you actually want to override.
@Override
public void close() throws IOException {
if (!closed) {
closed = true;
super.close();
delegate.close();
}
}
// You have to implement these abstract read methods. Easy, just delegate them.
// ...except you need to keep the stream position in sync.
@Override
public int read() throws IOException {
streamPos++;
return delegate.read();
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int read = delegate.read(b, off, len);
if (read > 0) {
streamPos += read;
}
return read;
}
// In a perfect world, the above should be all you need to do. Unfortunately, it's not.
// We need to keep the delegate position in sync with the position in this class.
// Overriding the seek method should do.
@Override
public void seek(long pos) throws IOException {
super.seek(pos); // Don't forget to call super here, as we rely on positions being in sync.
delegate.seek(pos);
}
// Some plugins require stream length, so we need to delegate that.
@Override
public long length() {
try {
// Unfortunately, this method does not declare IOException like the
// interface method does, so we need this strange try/catch here.
return delegate.length();
} catch (IOException e) {
// It's also possible to use a generics hack to throw a checked
// exception as unchecked. I leave that as an exercise...
throw new UndeclaredThrowableException(e);
}
}
// You may be able to skip the flush methods. If you do, skip both.
@Override
public void flushBefore(long pos) throws IOException {
delegate.flushBefore(pos);
}
@Override
public long getFlushedPosition() {
return delegate.getFlushedPosition();
}
// You could probably skip the methods below, as I don't think they are ever used as intended.
@Override
public boolean isCached() {
return delegate.isCached();
}
@Override
public boolean isCachedMemory() {
return delegate.isCachedMemory();
}
@Override
public boolean isCachedFile() {
return delegate.isCachedFile();
}
}
...虽然我认为以上内容涵盖了所有基础,但您可能应该对其进行测试。
除非您打算使用大量 try-with-resources 语句,否则您可能会发现一个简单的 try/catch(就像您已经拥有的那样)更具可读性。不过,我会将其提取为这样的方法:
static void close(Closeable closeable) throws IOException {
try {
closeable.close();
}
catch (IOException e) {
if (!"closed".equals(e.getMessage())) {
throw e;
}
// Otherwise, we're already closed, just ignore it,
}
}
请注意,依赖这样的异常消息 可能 在未来的 Java 版本中中断,如果有人决定需要更好的解释...