【发布时间】:2019-05-15 21:57:48
【问题描述】:
我想在 javax.servlet.Filter 中压缩响应体。这是我的代码
byte[] bytes = // compressing response body
response.addHeader("Content-Encoding", "gzip");
response.addHeader("Content-Length", String.valueOf(bytes.length));
response.setContentLength(bytes.length);
response.setBufferSize(bytes.length * 2);
ServletOutputStream output = response.getOutputStream();
output.write(bytes);
output.flush();
output.close();
但我在 Chrome 开发工具中看到的实际响应是
Accept-Ranges: bytes
Cache-Control: max-age=2592000
Content-Type: application/javascript;charset=UTF-8
Date: Fri, 14 Dec 2018 15:34:25 GMT
Last-Modified: Tue, 09 Oct 2018 13:42:54 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
我不希望 Transfer-Encoding: 分块,因为我声明了“Content-Length”。我在java上写了一个简单的测试
URLConnection connection = new URL("http://127.0.0.1:8081/js/ads.js").openConnection();
connection.addRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
connection.addRequestProperty("Accept-Encoding", "gzip, deflate");
connection.addRequestProperty("Accept-Language", "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7");
connection.addRequestProperty("Cache-Control", "no-cache");
connection.addRequestProperty("Connection", "keep-alive");
connection.addRequestProperty("Host", "127.0.0.1:8081");
connection.addRequestProperty("Pragma", "no-cache");
connection.addRequestProperty("Upgrade-Insecure-Requests", "1");
connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36");
connection.connect();
connection.getHeaderFields().forEach((s, strings) ->
System.out.println(s + ":" + String.join(",", strings)));
这是我发现的:
- 如果我评论设置“User-Agent”标头或将“User-Agent”更改为任何其他值,那么我会收到“Content-Length”的响应
- 如果 Chrome 上的“用户代理”点然后我得到 Transfer-Encoding: chunked。
我调试了 sun.nio.ch.SocketChannel#write 方法,它得到了正确的 ByteBuffers 和 Content-Length 标头值。
我无法理解这种对分块的神奇转变发生在哪里?
更新
奇怪的是,我将压缩字节写入 Socket(我确信当我调试到调用 SocketChannel 实现中的本机方法写入时)。但是如果我没有指定 User-Agent 标头或放置一些随机字符串,URLConnection 将返回我的 unzipped 字节数组和 Chrome 的 User-Agent 和正确的 gzip 字节数组。 SO 似乎在 Windows 套接字实现的某个地方发生了魔法。
【问题讨论】:
-
为什么将缓冲区大小设置为文件大小的两倍?另外,请尝试在输出流中删除对
flush的显式调用。 -
我不知道缓冲区是仅用于响应正文还是包含标头。所以我设置了足够的缓冲区大小。
-
删除冲洗没有帮助。我的调试显示 write 方法的调用调用了对 SocketChannel 的写入。
-
嗯。通常分块编码仅在您尝试执行“额外”操作时发生,例如在
OutputStream上调用flush()或close()。如果您设置Content-Length然后自己将文件转储到输出流中,Tomcat 确实应该尊重您的代码。 Tomcat版本?您的客户端和 Tomcat 之间是否有反向代理? -
缓冲区大小一般用于响应。除非您遇到一些奇怪的性能问题,否则通常没有理由设置响应缓冲区大小。更改响应缓冲区大小最终会永久更改该(重用)响应对象的响应缓冲区,因此如果您有一些大文件被返回,您最终可能会浪费大量堆保持较大的缓冲区.
标签: java tomcat content-length chunked