It seems like it the Spring RestTemplate
isn't able to stream a response directly to file without buffering it all in memory. What is the proper to achieve this using the newer Spring 5 WebClient
?
WebClient client = WebClient.create("https://example.com"); client.get().uri(".../{name}", name).accept(MediaType.APPLICATION_OCTET_STREAM) ....?
I see people have found a few workarounds/hacks to this issue with RestTemplate
, but I am more interested in doing it the proper way with the WebClient
.
There are many examples of using RestTemplate
to download binary data but almost all of them load the byte[]
into memory.
RestTemplate will still be used. But in some cases, the non-blocking approach uses much fewer system resources compared to the blocking one. So, WebClient is a preferable choice in those cases.
Because WebClient is immutable it is thread-safe. WebClient is meant to be used in a reactive environment, where nothing is tied to a particular thread (this doesn't mean you cannot use in a traditional Servlet application).
While Initialising WebClient As mentioned in the code block, whenever a 5XX/4XX Error occurs, we can throw a user defined exception, and then execute error handling logic based on those user defined exceptions. Once this error Handler is defined, we can add it in the WebClient Initialisation.
WebClient – retrieve() vs exchange()exchange method provides more control and details like status, headers and response body, etc. retrieve() method provides automatic error signal (e.g. 4xx and 5xx). No automatic error signal is available for exchange() method and we need to check status code and handle it.
With recent stable Spring WebFlux (5.2.4.RELEASE as of writing):
final WebClient client = WebClient.create("https://example.com"); final Flux<DataBuffer> dataBufferFlux = client.get() .accept(MediaType.TEXT_HTML) .retrieve() .bodyToFlux(DataBuffer.class); // the magic happens here final Path path = FileSystems.getDefault().getPath("target/example.html"); DataBufferUtils .write(dataBufferFlux, path, CREATE_NEW) .block(); // only block here if the rest of your code is synchronous
For me the non-obvious part was the bodyToFlux(DataBuffer.class)
, as it is currently mentioned within a generic section about streaming of Spring's documentation, there is no direct reference to it in the WebClient section.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With