如何在 JAVA servlet 中处理压缩 (gzip) HTTP 请求(不响应)- 简单示例?
How to handle compressed (gzip) HTTP requests (NOT response) in JAVA servlet - Simple Example?
我为这个问题苦苦挣扎了一段时间;在找到一个简单的解决方案之后......想问一个问题和答案!!
这个问题已经在堆栈溢出时以不同的方式被多次问到,accepted solutions
要么是 partially correct and complex
要么是谈论 response
压缩。
汇总关于此主题的一些旧问答:
接受错误ans/partially正确且复杂
随后的答案。
- How to decode Gzip compressed request body in Spring MVC:(几乎正确的第二个答案,但复杂的矫枉过正)
接受的答案是错误的。这是关于 RESPONSE
压缩而不是 REQUEST
.
类似问题 - 终止于“无请求压缩”
Spring RestTemplate 框架的具体问题和答案:
一个简单的解决方案是使用过滤器。 (See servlet-filter tutorial)
创建 Servlet 过滤器:
- 确保过滤器被调用 first/before 任何使用请求 body.
的过滤器
我。在 web.xml:
中注册过滤器
<filter>
<filter-name>GzipRequestFilter</filter-name>
<filter-class>com...pkg...GzipRequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GzipRequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
二.过滤器代码 class:
public class GzipRequestFilter implements Filter {
// Optional but recommended.
private static final Set<String> METHODS_TO_IGNORE = ImmutableSet.of("GET", "OPTIONS", "HEAD");
@Override
public void doFilter(
final ServletRequest request,
final ServletResponse response,
final FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String method = httpServletRequest.getMethod().toUpperCase();
String encoding = Strings.nullToEmpty(
httpServletRequest.getHeader(HttpHeaders.CONTENT_ENCODING));
if (METHODS_TO_IGNORE.contains(method) || !encoding.contains("application/gzip")) {
chain.doFilter(request, response); // pass through
return;
}
HttpServletRequestWrapper requestInflated = new GzippedInputStreamWrapper(httpServletRequest);
chain.doFilter(requestInflated, response);
}
@Override
public void init(final FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
三.后面是 GzipInputStream 包装器的代码:
// Simple Wrapper class to inflate body of a gzipped HttpServletRequest.
final class GzippedInputStreamWrapper extends HttpServletRequestWrapper {
private GZIPInputStream inputStream;
GzippedInputStreamWrapper(final HttpServletRequest request) throws IOException {
super(request);
inputStream = new GZIPInputStream(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
// NOTE: Later versions of javax.servlet library may require more overrides.
public int read() throws IOException {
return inputStream.read();
}
public void close() throws IOException {
super.close();
inputStream.close();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(inputStream));
}
}
现在剩下的就是如何发送压缩请求了?
Postman 尚不支持发送压缩的 HttpRequest
正文。您仍然可以使用 binary
选项使其工作,并使用包含正确编码请求的 gzip 文件 body.
一种方法是使用带有 pako
compression library. For a multipart/form-data request see form-data
library
的 nodejs 脚本
const pako = require('pako')
const axios = require('axios')
var params = qs.stringify({
'num': 42,
'str': 'A string param',
});
data = pako.gzip(Buffer.from(params));
var config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Encoding': 'application/gzip';
},
}
await axios.post(
'http://url-for-post-api-accepting-urlencoded',
data,
config,
).then((res) => {
console.log(`status: ${res.status} | data: ${res.data}`)
}).catch((error) => {
console.error(error)
})
备注:
- 我们使用
Content-Encoding: application/gzip
header 来指定压缩请求。是的,这是标准的。
- 不要使用
Content-Type
,因为它不适用于 multipart/form-data
。
- HTTP 协议运行假设 HttpRequests 的大小与 HttpResponses 相形见绌。
- 此外,由于假定 browser/client 端的计算能力有限,所以规范是压缩响应而不是请求。浏览器无法原生压缩,但可以原生解压。
- 但是,不幸的是,经过多年的许多开发人员推动代码;一些 HTTP API 发展到消耗大量 strings/data!!
- 让 java servlet 可以选择处理压缩请求是小菜一碟。
我为这个问题苦苦挣扎了一段时间;在找到一个简单的解决方案之后......想问一个问题和答案!!
这个问题已经在堆栈溢出时以不同的方式被多次问到,accepted solutions
要么是 partially correct and complex
要么是谈论 response
压缩。
汇总关于此主题的一些旧问答:
接受错误ans/partially正确且复杂 随后的答案。
- How to decode Gzip compressed request body in Spring MVC:(几乎正确的第二个答案,但复杂的矫枉过正)
接受的答案是错误的。这是关于
RESPONSE
压缩而不是REQUEST
.类似问题 - 终止于“无请求压缩”
Spring RestTemplate 框架的具体问题和答案:
一个简单的解决方案是使用过滤器。 (See servlet-filter tutorial)
创建 Servlet 过滤器:
- 确保过滤器被调用 first/before 任何使用请求 body. 的过滤器
我。在 web.xml:
中注册过滤器<filter>
<filter-name>GzipRequestFilter</filter-name>
<filter-class>com...pkg...GzipRequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GzipRequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
二.过滤器代码 class:
public class GzipRequestFilter implements Filter {
// Optional but recommended.
private static final Set<String> METHODS_TO_IGNORE = ImmutableSet.of("GET", "OPTIONS", "HEAD");
@Override
public void doFilter(
final ServletRequest request,
final ServletResponse response,
final FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String method = httpServletRequest.getMethod().toUpperCase();
String encoding = Strings.nullToEmpty(
httpServletRequest.getHeader(HttpHeaders.CONTENT_ENCODING));
if (METHODS_TO_IGNORE.contains(method) || !encoding.contains("application/gzip")) {
chain.doFilter(request, response); // pass through
return;
}
HttpServletRequestWrapper requestInflated = new GzippedInputStreamWrapper(httpServletRequest);
chain.doFilter(requestInflated, response);
}
@Override
public void init(final FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
三.后面是 GzipInputStream 包装器的代码:
// Simple Wrapper class to inflate body of a gzipped HttpServletRequest.
final class GzippedInputStreamWrapper extends HttpServletRequestWrapper {
private GZIPInputStream inputStream;
GzippedInputStreamWrapper(final HttpServletRequest request) throws IOException {
super(request);
inputStream = new GZIPInputStream(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
// NOTE: Later versions of javax.servlet library may require more overrides.
public int read() throws IOException {
return inputStream.read();
}
public void close() throws IOException {
super.close();
inputStream.close();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(inputStream));
}
}
现在剩下的就是如何发送压缩请求了?
Postman 尚不支持发送压缩的 HttpRequest
正文。您仍然可以使用 binary
选项使其工作,并使用包含正确编码请求的 gzip 文件 body.
一种方法是使用带有 pako
compression library. For a multipart/form-data request see form-data
library
const pako = require('pako')
const axios = require('axios')
var params = qs.stringify({
'num': 42,
'str': 'A string param',
});
data = pako.gzip(Buffer.from(params));
var config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Encoding': 'application/gzip';
},
}
await axios.post(
'http://url-for-post-api-accepting-urlencoded',
data,
config,
).then((res) => {
console.log(`status: ${res.status} | data: ${res.data}`)
}).catch((error) => {
console.error(error)
})
备注:
- 我们使用
Content-Encoding: application/gzip
header 来指定压缩请求。是的,这是标准的。 - 不要使用
Content-Type
,因为它不适用于multipart/form-data
。 - HTTP 协议运行假设 HttpRequests 的大小与 HttpResponses 相形见绌。
- 此外,由于假定 browser/client 端的计算能力有限,所以规范是压缩响应而不是请求。浏览器无法原生压缩,但可以原生解压。
- 但是,不幸的是,经过多年的许多开发人员推动代码;一些 HTTP API 发展到消耗大量 strings/data!!
- 让 java servlet 可以选择处理压缩请求是小菜一碟。