[SPRING] Spring RestTemplate으로 HTTP 요청을 압축하는 방법은 무엇입니까?
SPRINGSpring RestTemplate으로 HTTP 요청을 압축하는 방법은 무엇입니까?
org.springframework.web.client.RestTemplate에 의해 생성 된 HTTP 요청을 gzip하는 방법?
스프링 부트 1.3.5 (Java SE, 안드로이드 또는 웹 브라우저의 Javascript가 아닌)와 함께 Spring 4.2.6을 사용하고 있습니다.
정말 큰 POST 요청을 만들고 있는데 요청 본문을 압축하려고합니다.
해결법
-
==============================
1.스트리밍이없는 간단한 솔루션과 스트리밍을 지원하는 솔루션을 제안합니다.
스트리밍이없는 간단한 솔루션과 스트리밍을 지원하는 솔루션을 제안합니다.
스트리밍을 필요로하지 않는다면 Spring의 커스텀 ClientHttpRequestInterceptor를 사용하십시오.
RestTemplate rt = new RestTemplate(); rt.setInterceptors(Collections.singletonList(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);
GZip ClientHttpRequestFactory는 다음과 같습니다.
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); } }
그리고 압축 된 ClientHttpRequest는 다음과 같습니다.
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(); } }
마지막으로 Wrapper ClientHttpRequest는 다음과 같습니다.
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을 작성하기 때문입니다.
BTW 그것은 서버에 gzip 요청 압축을 거의 지원하지 않는 것 같습니다. 관련 항목 : WebRequest에서 gzipped 데이터 보내기? Zip Bomb 문제를 지적합니다. 나는 당신이 그것에 대한 몇 가지 코드를 작성해야 할 것이라고 생각합니다.
-
==============================
2.주요 아이디어는 requestCallback을 생성하는 것입니다. requestCallback은 gzipOutputStream에서 보내려는 데이터를 요청 스트림에 직접 복사합니다.
주요 아이디어는 requestCallback을 생성하는 것입니다. requestCallback은 gzipOutputStream에서 보내려는 데이터를 요청 스트림에 직접 복사합니다.
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);
모래밭:
-
==============================
3.위의 답변에서 @TestoTestini에서 ByteArrayOutputStream과 GZIPOutputStream이 closeable ()을 구현하기 때문에 Java 7 +의 'try-with-resources'구문을 활용하면 getGzip 함수를 다음과 같이 축소 할 수 있습니다.
위의 답변에서 @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의 원래 답변에 대해 언급하고 위의 코드 형식을 유지하는 방법을 찾을 수 없으므로이 답변을 참조하십시오).
from https://stackoverflow.com/questions/37415294/how-to-zip-compress-http-request-with-spring-resttemplate by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] 403 대신 봄 보안 익명 401 (0) | 2019.04.19 |
---|---|
[SPRING] 스프링 MVC PUT 요청이 405 메소드를 허용하지 않음을 반환합니다. (0) | 2019.04.19 |
[SPRING] Spring 4.3.0.RELEASE, Hibernate 5.0.9. 최종, SessionFactoryImplementor.getProperties 메소드 누락 (0) | 2019.04.19 |
[SPRING] Spring TransactionTemplate과 SimpleJdbcTemplate은 스레드로부터 안전한가요? (0) | 2019.04.19 |
[SPRING] Spring @DirtiesContext가 Spring 컨텍스트를 다시로드합니까? (0) | 2019.04.19 |