如何使用 Spring RestTemplate 压缩 HTTP 请求?
How to zip- compress HTTP request with Spring RestTemplate?
如何 gzip HTTP 请求,由 org.springframework.web.client.RestTemplate?
创建
我正在使用 Spring 4.2.6
和 Spring Boot 1.3.5
(Java SE,而不是网络浏览器中的 Android 或 Javascript) .
我正在发出一些非常大的 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
我copied
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();
}
}
此方法使用 chunked transfer encoding 创建一个请求,如果大小已知,可以更改设置内容长度 header。
ClientHttpRequestInterceptor
and/or 自定义 ClientHttpRequestFactory
方法的优势在于它适用于 RestTemplate 的任何方法。另一种方法,传递 RequestCallback is possible only with execute
方法,这是因为 RestTemplate 的其他方法在内部创建它们自己的 RequestCallback(s) 来生成内容。
顺便说一句,似乎有 little support to decompress gzip request on the server. Also related: Sending gzipped data in WebRequest? that points to the Zip Bomb issue. I think you will have to write some code。
根据@TestoTestini 的上述回答,如果我们利用 Java 7+ 的 'try-with-resources' 语法(因为 ByteArrayOutputStream
和 GZIPOutputStream
实现可关闭() ) 然后我们可以将 getGzip 函数缩小为以下内容:
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 的原始答案并保留上述代码格式的方法,因此这个答案)。
因为我不能评论@roj 的 post 我在这里写一个答案。
@roj 片段虽然很简洁,但实际上与@Testo Testini 的片段不一样。
德图关闭流之前:
byteStream.toByteArray();
在@rog 回答中,这发生在 stream.close()
之前,因为流位于 try/resource 块中。
如果需要使用try-with-resources,zipStream
应该在byteStream.toByteArray();
之前关闭
完整的片段应该是:
private byte[] getGzip(byte[] body) throws IOException {
try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
GZIPOutputStream zipStream = new GZIPOutputStream(byteStream)) {
zipStream.write(body);
zipStream.close();
byte[] compressedData = byteStream.toByteArray();
return compressedData;
}
}
出现错误(“压缩文件在到达 end-of-stream 标记之前结束”)并且上面修复了我的错误,我认为我应该分享这个。
如何 gzip HTTP 请求,由 org.springframework.web.client.RestTemplate?
我正在使用 Spring 4.2.6
和 Spring Boot 1.3.5
(Java SE,而不是网络浏览器中的 Android 或 Javascript) .
我正在发出一些非常大的 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
我copied
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();
}
}
此方法使用 chunked transfer encoding 创建一个请求,如果大小已知,可以更改设置内容长度 header。
ClientHttpRequestInterceptor
and/or 自定义 ClientHttpRequestFactory
方法的优势在于它适用于 RestTemplate 的任何方法。另一种方法,传递 RequestCallback is possible only with execute
方法,这是因为 RestTemplate 的其他方法在内部创建它们自己的 RequestCallback(s) 来生成内容。
顺便说一句,似乎有 little support to decompress gzip request on the server. Also related: Sending gzipped data in WebRequest? that points to the Zip Bomb issue. I think you will have to write some code。
根据@TestoTestini 的上述回答,如果我们利用 Java 7+ 的 'try-with-resources' 语法(因为 ByteArrayOutputStream
和 GZIPOutputStream
实现可关闭() ) 然后我们可以将 getGzip 函数缩小为以下内容:
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 的原始答案并保留上述代码格式的方法,因此这个答案)。
因为我不能评论@roj 的 post 我在这里写一个答案。
@roj 片段虽然很简洁,但实际上与@Testo Testini 的片段不一样。
德图关闭流之前:
byteStream.toByteArray();
在@rog 回答中,这发生在 stream.close()
之前,因为流位于 try/resource 块中。
如果需要使用try-with-resources,zipStream
应该在byteStream.toByteArray();
完整的片段应该是:
private byte[] getGzip(byte[] body) throws IOException {
try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
GZIPOutputStream zipStream = new GZIPOutputStream(byteStream)) {
zipStream.write(body);
zipStream.close();
byte[] compressedData = byteStream.toByteArray();
return compressedData;
}
}
出现错误(“压缩文件在到达 end-of-stream 标记之前结束”)并且上面修复了我的错误,我认为我应该分享这个。