【问题标题】:GZIPOutputStream not properly compressing a String for HTTP ResponseGZIPOutputStream 未正确压缩 HTTP 响应的字符串
【发布时间】:2012-02-29 07:29:49
【问题描述】:

我正在编写一个简单的 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();

【问题讨论】:

  • 大家好奇,你用的是哪个服务器?因为像这样的设置在服务器级别更容易完成。例如:对于tomcat,您必须为内容类型application/json 启用gzip 压缩,然后就完成了。或者您实际上是在自己编写服务器,正如您的第一句话所说的那样?
  • 您至少在最后一个响应标题行之后、内容之前缺少CRLF
  • 感谢 cmets 伙计们 - 我实际上是在编写自己的服务器,因为它真的只是一个简单的任务。我打开一个端口,只监听来自 Javascript JSONP 请求的请求。我希望这不会产生真正的安全隐患。关于 CRLF,我相信我在底部附近有:out.println();

标签: java gzip httprequest data-compression gzipoutputstream


【解决方案1】:

更新:这不再是正确答案,请参阅上面@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

【讨论】:

  • 谢谢!我不得不将 DeflaterInputStream 更改为 DeflaterOutputStream,但这就像一个魅力!在关闭服务器连接之前我应该​​等待什么?它似乎以这种方式工作,但正如你所说,我不希望发生过早的随机下降。
  • 实际上,如果您在关闭套接字之前刷新()输出流(在您的情况下为“out”),可能没问题。如果您对此答案满意,请将此答案标记为正确。
【解决方案2】:

接受的答案不正确。

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 就是其中之一。

【讨论】:

  • 这是正确答案。当我在下面写下我的原始答案时,仍然有不支持 gzip 编码(但确实支持 deflate)的浏览器(IE6 我在看你)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-06
  • 2012-02-12
  • 1970-01-01
  • 2015-12-29
  • 2021-03-23
  • 1970-01-01
相关资源
最近更新 更多