Inflater 和 Deflater 解压缩中的坑

发布时间 2023-04-05 01:28:36作者: 枯木fc

使用 Inflater 和 Deflater 解压缩可能会踩到一个死循环的坑。

 

在网上搜索 Inflater 和 Deflater 的 demo 时,往往都会看到类似下面的写法:

注意看 7-10 行的写法,假如我传一个不完整的压缩后的数据去解压(比如 main() 方法里的),那么 7-10 行就会出现一个死循环 

 1   public static byte[] uncompress(byte[] input) throws IOException, DataFormatException {
 2         Inflater inflater = new Inflater();
 3         inflater.setInput(input);
 4         ByteArrayOutputStream baos = new ByteArrayOutputStream(input.length);
 5 
 6         byte[] buff = new byte[1024];
       // 注意这里的写法,有死循环的风险
7 while (!inflater.finished()) { 8 int count = inflater.inflate(buff); 9 baos.write(buff, 0, count); 10 } 11 12 baos.close(); 13 inflater.end(); 14 return baos.toByteArray(); 15 } 16 17 public static byte[] compress(byte[] data) throws IOException { 18 Deflater compress = new Deflater(); 19 20 compress.reset(); 21 compress.setInput(data); 22 compress.finish(); 23 ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length); 24 25 byte[] buf = new byte[1024]; 26 while (!compress.finished()) { 27 int i = compress.deflate(buf); 28 bos.write(buf, 0, i); 29 } 30 bos.close(); 31 32 compress.end(); 33 return bos.toByteArray(); 34 } 35
    // 用来复现死循环的例子 36 public static void main(String[] args) throws IOException, DataFormatException { 37 byte[] after = compress("xxxxxx".getBytes(StandardCharsets.UTF_8)); 38 int len = after.length - 1; 39 byte[] newByte = new byte[len]; 40 System.arraycopy(after, 0, newByte, 0, len); 41 after = uncompress(newByte); 42 System.out.println(new String(after)); 43 }

 

所以 7-10 行的逻辑需要加一些判断,比如增加对返回值的判断,及时跳出循环

看 inflate() 方法的注释可知,当返回值是 0 时,表示需要调用 needsInput() 或者 needsDictionary() 来判断是否需要继续追加数据或者需要字典

继续看 inflate(byte[] b) -> inflate(byte[] b, int off, int len) -> inflateBytes(long addr, byte[] b, int off, int len) 的 JDK 源码注释和内容可知,当解压出现其它异常时也会返回 0,因此这里个人认为, needsInput() 或者 needsDictionary()  均为 false 时,也有概率返回 0。

1     while (!inflater.finished()) {
2             int count = inflater.inflate(buff);
3             if (count == 0) {
4                 break;
5             }
6             baos.write(buff, 0, count);
7         }

 

综上,虽然网上这两工具类的 demo 都基本一样,但都还是有风险的,需要多加小心

外网有帖子提到过这个问题:Java Inflater will loop infinitely sometimes