在Java中解压缩GZip字符串

16

我可以找到很多函数用来解压GZip文件,但是如何解压GZip字符串呢?

我试图解析一个HTTP响应,其中响应主体使用GZip压缩。然而,整个响应只是以字符串的形式存储,因此字符串的一部分包含二进制字符。

我尝试使用:

byte responseBodyBytes[] = responseBody.getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(responseBodyBytes); 
GZIPInputStream gzis = new GZIPInputStream(bais);

但是这样只会抛出一个异常:java.io.IOException: Not in GZIP format(不是GZIP格式)。


这个回答解决了你的问题吗?GZIPInputStream转换为String - Yash
3个回答

15

没有所谓的GZip字符串。GZip是二进制格式,而字符串是文本。

如果你想压缩一个字符串,你需要先将其转换为二进制格式,例如使用一个OutputStream作为压缩器,然后将OutputStreamWriter链接到它上面(例如GZIPOutputStream

同样地,如果您要读取数据,则可以使用将InputStreamReader链接到解压缩的InputStream(例如GZIPInputStream)

Reader中轻松读取的一种方法是使用CharStreams.toString(Readable) (来自Guava或类似的库)。


1
我正在尝试解析一个使用GZip压缩的HTTP响应。然而,整个响应只是存储在一个字符串中,因此该字符串的一部分包含二进制字符。您是说无法将这个“GZip字符串”转换为文本字符串吗? - Matt
@Matt:你不应该一开始就将响应存储在字符串中。如果它是二进制的,那么根本不应该是文本,除非它是base64编码。"字符串的一部分包含二进制数据"这个概念真的行不通。听起来你需要改变你的方法。 - Jon Skeet
响应最初以byte[]的形式呈现,因此这是我可用的全部内容。我可以使用它吗? - Matt
@Jon Skeet,我现在也遇到了同样的问题。您会建议将响应存储在 byte[] 中吗? - Amir Rachum
@Amir:我不知道你想做什么,所以很难说。我建议你在一个新问题中提供更多的上下文信息。 - Jon Skeet
@Jon https://dev59.com/R1bTa4cB1Zd3GeqP_oon - Amir Rachum

1
理想情况下,您应该使用高级库来处理这些内容。这样,每当发布新版本的HTTP时,库维护者希望为您完成所有繁重的工作,并且您只需要更新库的版本即可。 除此之外,尝试自己做这件事是一个很好的练习。 假设您正在从TCP套接字中读取一组字节的HTTP响应。如果没有gzip编码,则将整个响应放入字符串中可能有效。然而,"Content-Encoding: gzip"头的存在意味着响应体将(如您所指出的)是二进制的。 您可以将响应主体的起始字节标识为第一个出现的String序列"\r\n\r\n"(或4个字节0x0d、0x0a、0x0d、0x0a)后面的第一个字节。 gzip编码有一个特殊的头部,您应该测试前3个主体字节:
                byte[] buf;  // from the HTTP Response stream
                // ... insert code here to populate buf from HTTP Response stream
                // ...
                int bodyLen = 1234;  // populate this value from 'Content-length' header
                int bodyStart = 123; // index of byte buffer where body starts
                if (bodyLen > 4 && buf[bodyStart] == 0x1f && buf[bodyStart + 1] == (byte) 0x8b && buf[bodyStart + 2] == 0x08) {
                    // gzip compressed body
                    ByteArrayInputStream bais = new ByteArrayInputStream(buf);
                    if (bodyStart > 0) bais.skip(bodyStart);

                    // Decompress the bytes
                    byte[] decompressedBytes = new byte[bodyLen * 4];
                    int decompressedDataLength = 0;
                    try {
                        // note: replace this try-catch with try-with-resources here where possible
                        GZIPInputStream gzis = new GZIPInputStream(bais);
                        decompressedDataLength = gzis.read(decompressedBytes);
                        gzis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

如果前3个字节不匹配GZIP头的魔术值,GZIPInputStream将产生“不是GZIP格式”的错误,因此测试这些值将有助于解决您的特定问题。
此外,在GZIP格式中还有一个CRC校验和,但如果缺少或不正确,则应看到不同的错误。

0

也许这可以帮助:

try (final GZIPInputStream gzipInput = new GZIPInputStream(new ByteArrayInputStream(compressedByteArray));
        final StringWriter stringWriter = new StringWriter()) {
        org.apache.commons.io.IOUtils.copy(gzipInput, stringWriter, "UTF_8");
        String decodedString = stringWriter.toString();
    } catch (IOException e) {
        throw new UncheckedIOException("Error while decompression!", e);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接