I am trying to stream the result of a file download directly into another post using spring's RestTemplate
My current approach is the following:
ResponseEntity<InputStreamResource> downloadResponse = restTemplate.getForEntity(fileToDownloadUri, InputStreamResource.class);
InputStreamResource imageInputStreamResource = downloadResponse.getBody();
ResponseEntity<String> response = restTemplate.exchange(storageUri, POST, new HttpEntity<>(imageInputStreamResource), String.class);
However, I get the following exception running the code above:
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://host:port/path/some.jpg": stream is closed; nested exception is java.io.IOException: stream is closed
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:6
...
Caused by: java.io.IOException: stream is closed
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.ensureOpen(HttpURLConnection.java:3348)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3373)
It seems that the response is always closed as the final step of processing. With the response, the HttpURLConnection
is closed, and the stream is no longer processable.
I would like to be able to implement this scenario without having to hold the file completely in memory or writing it to a file (as described here).
Any hints are highly appreciated.
Since you tell RestTemplate to expect InputStreamResource it will try and use an appropriate converter to convert your message to a InputStreamResource. ( I'm guessing there is none that handles this as you want )
You should be able to let it expect a Resource from where you can get an input stream and read that.
using this you can write the response to somewhere else.
Files.write(inputStream, new File("./test.json"));
wrote the file for me, so I assume the inputstream can also be used somewhere else. ( I used Spring 4.3.5 )edit:
As the OP states, this will still load the file in memory. Behind the scene the InputStream is a ByteArrayInputStream.
The default RestTemplate and MessageConverters are not made for streaming content at all. You could write your own implementation of a
org.springframework.web.client.ResponseExtractor
and maybe a MessageConverter. In ResponseExtractor you have access to theorg.springframework.http.client.ClientHttpResponse
imho for your use case, you might be better of using Apache Httpcomponents HttpClient where you find
HttpEntity#writeTo(OutputStream)
.If you want to forward the response directly without ever holding it in memory, you have to directly write to the response: