我可以找到很多可以解压缩 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 字符串这样的东西。 GZip 是二进制的,字符串是文本。
如果要压缩字符串,则需要先将其转换为二进制 - 例如
OutputStreamWriter
链接到压缩 OutputStream
(例如 GZIPOutputStream
)
与读取数据类似,您可以使用链接到解压
InputStreamReader
的InputStream
(例如GZIPInputStream
)。
轻松读取
Reader
的一种方法是使用 Guava中的
CharStreams.toString(Readable)
或类似的库。
理想情况下,您应该使用高级库来为您处理这些事情。这样,每当发布新版本的 HTTP 时,库维护者都会为您完成所有艰苦的工作,而您只需要更新版本的库即可。
除此之外,尝试自己做也是一个很好的练习。
假设您正在从 TCP 套接字读取 HTTP 响应作为字节流。 如果没有 gzip 编码,则可以将整个响应放入字符串中。 然而,“Content-Encoding:gzip”标头的存在意味着响应正文将(如您所述)是二进制的。
您可以将响应正文的开始标识为字符串序列第一次出现之后的第一个字节“ “(或 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 会产生“Not in GZIP format”错误,因此测试这些将有助于解决您的特定问题。
GZIP 格式中还有一个 CRC 校验和,但是如果丢失或不正确,您应该会看到不同的错误。
这可能有帮助:
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);
}
Gzip 内容是二进制的。如果你有一个字符串,该字符串的生成者必然使用某种编码。这不太可能是 UTF-8,因为违规的风险太大。它可能是 base64,尽管这会使压缩字节膨胀到 133%。
假设编码使用单字节字符集,如众所周知的 iso-8859-1,它应该毫无例外地被解码。所以这里还有一些其他的编码。您需要向我们展示字节。