GZIPOutputStream没有正确压缩HTTP响应的字符串

时间:2012-02-29 07:29:49

标签: java gzip httprequest data-compression gzipoutputstream

我正在编写一个简单的Java http服务器,它响应JSON数据。我正在尝试在发送之前对数据进行GZip,但它通常会发回gzip数据,这会在浏览器中产生错误。例如,在Firefox中它说:

内容编码错误 您尝试查看的页面无法显示,因为它使用的是无效或不受支持的压缩形式。

有时候,如果我正在压缩的字符串很小而没有某些字符,它会起作用,但是当有括号等时它似乎搞乱了。特别是,我在下面的示例文本失败了。

这是某种字符编码问题吗?我尝试了各种各样的东西,但它只是不想轻松工作。

String text;            
private Socket server;
DataInputStream in = new DataInputStream(server.getInputStream());
PrintStream out = new PrintStream(server.getOutputStream());

while ((text = in.readLine()) != null) {
    // ... process header info
    if (text.length() == 0) break;
}

out.println("HTTP/1.1 200 OK");
out.println("Content-Encoding: gzip");
out.println("Content-Type: text/html");
out.println("Connection: close");


// x is the text to compress
String x = "jsonp1330xxxxx462022184([[";
ByteArrayOutputStream outZip = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outZip);

byte[] b = x.getBytes(); // Changing character encodings here makes no difference

gzip.write(b);
gzip.finish();
gzip.close();
outZip.close();
out.println();
out.print(outZip);
server.close();

2 个答案:

答案 0 :(得分:2)

更新:这不再是正确的答案,请参阅上面@amichair的回答。

反直觉地说,我不认为GZIPOutputStream适合流媒体。试试这个:

...
out.println("Content-Encoding: deflate");  // NOTICE deflate encoding
out.println("Content-Type: text/html");
out.println("Connection: close");
out.println();
String x = "jsonp1330xxxxx462022184([[";
DeflaterInputStream dis = new DeflaterInputStream(out);
dis.write(x.getBytes("utf-8"));   // JSON is UTF-8
dis.close();
server.close(); //  this a bad idea, the client may not have read the data yet

答案 1 :(得分:1)

接受的答案不正确。

GZIPOutputStream确实可用于在HTTP中实现gzip内容编码。实际上,这正是我在JLHTTP轻量级HTTP服务器中实现它的方式。对deflate内容编码的支持是相同的,只需使用DeflaterOutputStream即可。上面代码的问题只是它的错误: - )

  • 所有println语句(包括底部的语句)应替换为print,并在字符串末尾添加显式\r\n。这是因为println打印的换行符与平台有关,因此例如在Linux上它只会打印\n,而HTTP需要一个完整的CRLF(\r\n)。

  • out.print(outZip)基本上调用outZip.toString()并将其打印到流中。但是,outZip包含压缩的二进制数据,因此将其转换为字符串(使用任意平台默认编码,不能少),很可能会破坏数据。

  • 代码获取字符串,将其转换为字节,压缩它们,将它们转换回字符串,将它们转换回字节并将其写出。相反,它只需要将字符串转换为字节,压缩它们并将它们写出来。您也不需要ByteArrayOutputStreamGZIPOutputStream可以直接包装基础输出流。只是不要忘记在标题之后刷新打印流(并且尾随CRLF),然后才开始使用主体的压缩流。

  • 关闭资源应该在finally或try-with-resources块中完成,并且顺序和时间正确。

  • 在此示例中,连接在流的末尾关闭,这很好。但一般来说,如果你想让连接保持活动状态并传输未知长度的潜在大数据(你事先不知道压缩的大小),你也需要实现chunked传输编码(它很漂亮)简单)。

修复代码后,GZIPOutputStream就像魅力一样。

但是,虽然非常适合教育目的,但请注意,这是 HTTP服务器,即使已修复。您可以进一步阅读RFC 2616或7230以了解还需要哪些HTTP ...但为什么要重新发明?有一堆轻量级的可嵌入HTTP服务器,您可以用它们轻松地完成工作,其中JLHTTP