HTTPをgzipで圧縮する方法request、org.springframework.web.client.RestTemplate?
によって作成されました
Spring Spring 4.2.6
でBoot 1.3.5
を使用しています(Java SE、AndroidまたはWebブラウザーのJavascriptではありません))。
非常に大きなPOST
リクエストを行っていますが、リクエスト本文を圧縮したいと思います。
私は2つのソリューションを提案します。1つはストリーミングなしのより単純なもので、もう1つはストリーミングをサポートするものです。
ストリーミングを必要としない場合、Spring機能であるカスタム ClientHttpRequestInterceptor
を使用します。
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
I コピー
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();
}
}
このアプローチでは、 チャンク転送エンコーディング でリクエストが作成されます。サイズがわかっている場合は、コンテンツの長さヘッダーを設定してこれを変更できます。
ClientHttpRequestInterceptor
および/またはカスタムClientHttpRequestFactory
アプローチの利点は、RestTemplateの任意のメソッドで機能することです。 RequestCallback を渡す別のアプローチは、 execute
メソッドでのみ可能です。これは、RestTemplateの他のメソッドが内部で独自のRequestCallbackを作成して生成するためです。コンテンツ。
ところで、 サーバーでgzipリクエストを解凍するためのサポートはほとんどない があるようです。また、関連: WebRequestでgzip圧縮されたデータを送信しますか? これは Zip Bomb の問題を示しています。私はあなたがそれのために いくつかのコード を書かなければならないと思います。
主なアイデアは、requestCallback
を作成することです。これにより、送信するデータがgzipOutputStream
からrequest
ストリームに直接コピーされます。
RequestCallback requestCallback = new RequestCallback() {
@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
GZIPOutputStream gzipOutputStream;
try {
gzipOutputStream = new GZIPOutputStream(request.getBody());
} catch (IOException ignored) {
return;
}
request.getHeaders().add("Content-Type", "application/octet-stream");
request.getHeaders().add("Content-Encoding", "gzip");
try {
String data = "Test data.";
gzipOutputStream.write(data.getBytes(StandardCharsets.UTF_8));
gzipOutputStream.flush(); // Optional in this example.
gzipOutputStream.finish();
} catch (IOException ignored) {
}
}
};
これで、次の方法で使用できます。
RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
ResponseExtractor<String> responseExtractor = new HttpMessageConverterExtractor<>(String.class,
restTemplate.getMessageConverters());
String response = restTemplate.execute("http://localhost:8080/gzip.php", HttpMethod.POST, requestCallback,
responseExtractor);
System.out.println(response);
リンク:
@TestoTestiniからの上記の回答に加えて、ByteArrayOutputStream
とGZIPOutputStream
の両方がcloseable()を実装しているため、Java 7+の 'try-with-resources'構文を利用すると、 getGzip関数を次のように記述します。
_private byte[] getGzip(byte[] body) throws IOException {
try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) {
try (GZIPOutputStream zipStream = new GZIPOutputStream(byteStream)) {
zipStream.write(body);
}
byte[] compressedData = byteStream.toByteArray();
return compressedData;
}
}
_
(@TestoTestiniの元の回答にコメントし、上記のコード形式を保持する方法が見つかりませんでした。したがって、この回答)。