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();
}