Java 使用try-with-resource语法关闭GZIPOutputStream,返回Byte[]二进制数据不正确问题
try-with-resource语法
try(xxxStream is=new xxxxStream()){
return is.xxx;
}
try-with语法实际上就是 try-finally对于流处理的一个语法糖,会在try的代码块执行完毕后自动添加Finally方法块,并调用流的Close方法。这么看来使用try-with-resources来处理gzip流并没有问题。
使用try-with 处理gzip流
问题
gzip会在close方法中调用finish方法把结果输出。
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(out)) {
gzip.write(str.getBytes(encoding));
return out.toByteArray();
}
使用上面写法,其实等同于下面的写法
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(out);
gzip.write(str.getBytes(encoding));
return out.toByteArray();
}finally{
gzip.close();
out.close();
}
就会发生下面错误
with code GZIP_UNCOMPRESS_EXCEPTION exception. null
--->java.io.EOFException: Unexpected end of ZLIB input stream
原因
return out.toByteArray();
// java.io.ByteArrayOutputStream.java
public synchronized byte toByteArray()[] {
return Arrays.copyOf(buf, count);
}
发生的原因发生在 这个地方,try-finally 会在 进入finally方法块之前将return 结果压栈,执行完毕后再返回栈内值。toByteArray作为作为一个Method就会在进入finally块之前被调用,这样子就会把结果压栈。等到finally执行完毕后再返回结果,这里返回的是引用值,如果是返回数组的引用其实这里也没有问题。关键是在toByteArray的时候copy了一下,这样子返回就是一个新的数组引用。导致返回结果的值不对。
结果复盘
/**
* 0 null null null 4 null null null null null
* 0 null null null null null null null null null
*
**/
static class FinallyTestClass implements Closeable {
public String[] values = new String[10];
public void setValue(int i, String v) {
values[i] = v;
}
public String[] getValues() {
String[] vs = new String[10];
System.arraycopy(values, 0, vs, 0, 10);
return vs;
}
@Override
public void close() throws IOException {
values[4] = "4";
for (String v : values) {
System.out.print(v + " ");
}
System.out.println();
}
}
public String[] tryFinallyTest() throws IOException {
try (FinallyTestClass f = new FinallyTestClass()) {
f.setValue(0, "0");
return f.getValues();
}
}
@Test
public void finallyTest() throws IOException {
String[] vs = tryFinallyTest();
for (String v : vs) {
System.out.print(v + " ");
}
System.out.println();
}
/**
* 0 null null null 4 null null null null null
* 0 null null null 4 null null null null null
*
**/
static class FinallyTestClass implements Closeable {
public String[] values = new String[10];
public void setValue(int i, String v) {
values[i] = v;
}
public String[] getValues() {
return values;
}
@Override
public void close() throws IOException {
values[4] = "4";
for (String v : values) {
System.out.print(v + " ");
}
System.out.println();
}
}
public String[] tryFinallyTest() throws IOException {
try (FinallyTestClass f = new FinallyTestClass()) {
f.setValue(0, "0");
return f.getValues();
}
}
@Test
public void finallyTest() throws IOException {
String[] vs = tryFinallyTest();
for (String v : vs) {
System.out.print(v + " ");
}
System.out.println();
}