Reputation:
I am trying to POST a large file from one microservice to another using spring rest template POST w/ custom interceptor as follows:
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate = new RestTemplate(requestFactory);
restTemplate.getInterceptors().add({customInterceptor});
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", {InputStreamResource});
body.add("metadata", {JSON string});
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
restTemplate.exchange({url}, HttpMethod.POST, requestEntity, ...);
(It makes no difference whether I use a SimpleClientHttpRequestFactory or HttpComponentsClientHttpRequestFactory)
Adding an interceptor results in the creation of an new InterceptingClientHttpRequestFactory (which wraps the original request factory) on a call to getRequestFactory.
This works fine for smaller files but for large files - since requests are never delegated to the original request factory, no streaming ever occurs and, hence, results in a java.lang.OutOfMemoryError: Java heap space
exception.
Any help would be appreciated.
Upvotes: 4
Views: 1387
Reputation: 330
I have also come across this issue, but switching to WebClient
is not an option at this point in time so I opted for a work around by implementing a new ClientHttpRequestFactory
(albeit via an AbstractClientHttpRequestFactoryWrapper
); it mainly works due to the fact that we are only tweaking the headers and not the body.
SimpleClientHttpRequestFactory simpleRequestFactory =
new SimpleClientHttpRequestFactory();
simpleRequestFactory.setConnectTimeout(10000);
simpleRequestFactory.setReadTimeout(60000);
simpleRequestFactory.setBufferRequestBody(false); // this enables streaming
SomeHeaderInterceptingClientHttpRequestFactory interceptingRequestFactory =
new SomeHeaderInterceptingClientHttpRequestFactory(simpleRequestFactory);
RestTemplate restTemplate = new RestTemplate(interceptingRequestFactory);
and this is what the SomeHeaderInterceptingClientHttpRequestFactory
looks like:
public class SomeHeaderInterceptingClientHttpRequestFactory
extends AbstractClientHttpRequestFactoryWrapper {
// you can have fields here that are initialized in the constructor
// e.g. a service that supplies the header value that you want to populate
public SomeHeaderInterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
// initialize fields
}
@Override
protected ClientHttpRequest createRequest(URI uri,
HttpMethod httpMethod,
ClientHttpRequestFactory requestFactory)
throws IOException {
ClientHttpRequest request = requestFactory.createRequest(uri, httpMethod);
HttpHeaders headers = request.getHeaders();
headers.set("SOME_HEADER", "some value");
return request;
}
}
If you want to do something to the body then you could also try to implement a new ClientHttpRequest
. You will then return a new instance of your newly introduced ClientHttpRequest
in the createRequest
method of your newly introduced ClientHttpRequestFactory
.
Upvotes: 0
Reputation: 2554
It seems this problem of RestTemplate will not be fixed according to this issue
we're not going to support this, now that the WebClient is available and provides first-class support for streaming.
Note that WebClient
is available in Spring 5.
Upvotes: 1