一个简单的解决方案是使用过滤器。 (See servlet-filter tutorial)
创建一个 Servlet 过滤器:
- 确保首先/在任何使用请求正文的过滤器之前调用过滤器。
我。在 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>
二。过滤器类代码:
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 压缩文件来使其工作。
一种方法是使用带有pako compression library 的nodejs 脚本。对于多部分/表单数据请求,请参阅 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 标头来指定请求被压缩。是的,这是标准的。
- 不要使用
Content-Type,因为它不适用于multipart/form-data。
- HTTP 协议一直在假设 HttpRequest 的大小与 HttpResponse 相比相形见绌。
- 此外,由于假定浏览器/客户端的计算能力有限,标准是压缩响应而不是请求。浏览器不能原生压缩,但可以原生解压。
- 但是,不幸的是,在许多开发人员推动代码多年之后;一些 HTTP API 演变为消耗大量字符串/数据!!
- 让 java servlet 可以选择处理压缩请求是小菜一碟。