如何使用Spring RestTemplate压缩HTTP请求?
如何 gzip HTTP 请求,由org.springframework.web.client.RestTemplate?
我正在与Spring一起使用(Java SE,而不是Web浏览器中的Android或Javascript)。Spring 4.2.6
Boot 1.3.5
我正在提出一些非常大的请求,我希望压缩请求正文。POST
如何 gzip HTTP 请求,由org.springframework.web.client.RestTemplate?
我正在与Spring一起使用(Java SE,而不是Web浏览器中的Android或Javascript)。Spring 4.2.6
Boot 1.3.5
我正在提出一些非常大的请求,我希望压缩请求正文。POST
我提出了两种解决方案,一种是没有流式传输的简单解决方案,另一种是支持流式传输的解决方案。
如果您不需要流式传输,请使用自定义 ClientHttpRequestInterceptor
,这是一项 Spring 功能。
RestTemplate rt = new RestTemplate();
rt.setInterceptors(Collections.singletonList(interceptor));
其中可能:interceptor
ClientHttpRequestInterceptor interceptor = new ClientHttpRequestInterceptor() {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
request.getHeaders().add("Content-Encoding", "gzip");
byte[] gzipped = getGzip(body);
return execution.execute(request, gzipped);
}
}
getGzip
我复制了
private byte[] getGzip(byte[] body) throws IOException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
try {
GZIPOutputStream zipStream = new GZIPOutputStream(byteStream);
try {
zipStream.write(body);
} finally {
zipStream.close();
}
} finally {
byteStream.close();
}
byte[] compressedData = byteStream.toByteArray();
return compressedData;
}
配置拦截器后,所有请求都将被压缩。
此方法的缺点是它不支持流式处理,因为 将内容作为ClientHttpRequestInterceptor
byte[]
如果需要流式传输,请创建自定义 ,例如 ,然后按如下方式使用它:ClientHttpRequestFactory
GZipClientHttpRequestFactory
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
ClientHttpRequestFactory gzipRequestFactory = new GZipClientHttpRequestFactory(requestFactory);
RestTemplate rt = new RestTemplate(gzipRequestFactory);
在哪里:GZipClientHttpRequestFactory
public class GZipClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
public GZipClientHttpRequestFactory(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
}
@Override
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory)
throws IOException {
ClientHttpRequest delegate = requestFactory.createRequest(uri, httpMethod);
return new ZippedClientHttpRequest(delegate);
}
}
并且是:ZippedClientHttpRequest
public class ZippedClientHttpRequest extends WrapperClientHttpRequest
{
private GZIPOutputStream zip;
public ZippedClientHttpRequest(ClientHttpRequest delegate) {
super(delegate);
delegate.getHeaders().add("Content-Encoding", "gzip");
// here or in getBody could add content-length to avoid chunking
// but is it available ?
// delegate.getHeaders().add("Content-Length", "39");
}
@Override
public OutputStream getBody() throws IOException {
final OutputStream body = super.getBody();
zip = new GZIPOutputStream(body);
return zip;
}
@Override
public ClientHttpResponse execute() throws IOException {
if (zip!=null) zip.close();
return super.execute();
}
}
最后是:WrapperClientHttpRequest
public class WrapperClientHttpRequest implements ClientHttpRequest {
private final ClientHttpRequest delegate;
protected WrapperClientHttpRequest(ClientHttpRequest delegate) {
super();
if (delegate==null)
throw new IllegalArgumentException("null delegate");
this.delegate = delegate;
}
protected final ClientHttpRequest getDelegate() {
return delegate;
}
@Override
public OutputStream getBody() throws IOException {
return delegate.getBody();
}
@Override
public HttpHeaders getHeaders() {
return delegate.getHeaders();
}
@Override
public URI getURI() {
return delegate.getURI();
}
@Override
public HttpMethod getMethod() {
return delegate.getMethod();
}
@Override
public ClientHttpResponse execute() throws IOException {
return delegate.execute();
}
}
此方法使用分块传输编码创建请求,如果大小已知,则可以更改此设置内容长度标头。
和/或自定义方法的优点是它可以与 RestTemplate 的任何方法一起使用。另一种方法是,传递 RequestCallback 只能通过 execute
方法,这是因为 RestTemplate 的其他方法在内部创建自己的 RequestCallback 来生成内容。ClientHttpRequestInterceptor
ClientHttpRequestFactory
顺便说一句,似乎很少支持在服务器上解压缩gzip请求。还相关:在WebRequest中发送gzip压缩数据?指向Zip Bomb问题。我认为你必须为它写一些代码。
进一步@TestoTestini的上述答案,如果我们利用Java 7 +的“try-with-resources”语法(因为两者都实现了closeable()),那么我们可以将getGzip函数缩小到以下内容:ByteArrayOutputStream
GZIPOutputStream
private byte[] getGzip(byte[] body) throws IOException {
try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
GZIPOutputStream zipStream = new GZIPOutputStream(byteStream)) {
zipStream.write(body);
byte[] compressedData = byteStream.toByteArray();
return compressedData;
}
}
(我找不到一种方法来评论@TestoTestini的原始答案并保留上述代码格式,因此这个答案)。