问题:在使用
GZIP压缩和解压缩
时可能会出现java.util.zip.ZipException: Not in GZIP format异常。
原因:在使用GZIP进行压缩时,创建GZIPOutputStream对象时,会调用一个writeHeader方法,此方法会在输出流中写入GZIP的头信息。代码如下:
private void writeHeader() throws IOException {
out.write(new byte[] {
(byte) GZIP_MAGIC, // Magic number (short)
(byte)(GZIP_MAGIC >> 8), // Magic number (short)
Deflater.DEFLATED, // Compression method (CM)
0, // Flags (FLG)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Extra flags (XFLG)
0 // Operating system (OS)
});
}
头部信息中前两个字节用于存储魔数,GZIP_MAGIC值为35615,第一个字节存储低位,第二个字节存储高位。在解压缩时GZIPInputStream会调用readheader读取头信息。代码如下:
private int readHeader(InputStream this_in) throws IOException {
CheckedInputStream in = new CheckedInputStream(this_in, crc);
crc.reset();
// Check header magic
if (readUShort(in) != GZIP_MAGIC) {
throw new ZipException("Not in GZIP format");
}
// Check compression method
if (readUByte(in) != 8) {
throw new ZipException("Unsupported compression method");
}
// Read flags
int flg = readUByte(in);
// Skip MTIME, XFL, and OS fields
skipBytes(in, 6);
int n = 2 + 2 + 6;
// Skip optional extra field
if ((flg & FEXTRA) == FEXTRA) {
int m = readUShort(in);
skipBytes(in, m);
n += m + 2;
}
// Skip optional file name
if ((flg & FNAME) == FNAME) {
do {
n++;
} while (readUByte(in) != 0);
}
// Skip optional file comment
if ((flg & FCOMMENT) == FCOMMENT) {
do {
n++;
} while (readUByte(in) != 0);
}
// Check optional header CRC
if ((flg & FHCRC) == FHCRC) {
int v = (int)crc.getValue() & 0xffff;
if (readUShort(in) != v) {
throw new ZipException("Corrupt GZIP header");
}
n += 2;
}
crc.reset();
return n;
}
GZIPInputStream首先通过readUShort读取前两个字节然后与魔数进行比较,如果相同表示当前字节数组为GZIP格式的字节数组,如果不相同则表示当前字节数组不是GZIP格式的数组于是会抛出异常java.util.zip.ZipException: Not in GZIP format。
结论:在使用GZIP进行压缩和解压时,如果压缩后的
字节数组在传输过程中发生改变
就会导致此异常的发生,所以如果不是直接传输压缩后的字节数组而是字符串时,在转换为字符串时,一定要使用ISO-8859-1这样的单字节编码,否则在将字符串转换为字节数组时会导致节数组产生变化,从而产生该异常。
版权声明:本文为asty9000原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。