Issue
The aim of my code is to retrieve an image from a third-party service.
I struggled a little for endpoint of download to work and only partially succeeded. When I call the endpoint via postman the answer is a .bin file, but what I need is to have a .png file. The greatest success is being able to get a .png file being able to customize the name as well. But personalization of the is not strictly necessary.
The project is built with the initializer and has the following dependencies:
- spring-boot-starter-web;
- lombok
- spring-boot-starter-webflux
- reactor-spring
Below is the source code of my endpoint:
@GetMapping("/retrieve-image")
public Mono<byte[]> retrieveImage(ImageRequest request) throws ExecutionException, InterruptedException, IOException {
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("attribute", request.getAttribute()); // fake for query string setting.
Mono<byte[]> image = webClient
.get()
.uri(uriBuilder -> uriBuilder
.path(Endpoint.THIRD_PARTY_SERVICE_URI)
.queryParams(queryParams)
.build())
.accept(MediaType.valueOf(String.valueOf(MediaType.IMAGE_PNG)))
.exchange()
.flatMap(clientResponse -> clientResponse.bodyToMono(byte[].class)
.doOnSuccess(body -> {
if (clientResponse.statusCode().isError()) {
log.error("HttpStatusCode = {}", clientResponse.statusCode());
log.error("HttpHeaders = {}", clientResponse.headers().asHttpHeaders());
log.error("ResponseBody = {}", body);
}
}));
return image;
}
Solution
You can also add the mime type of the file to the produces section of the @GetMapping
annotation, it should look something like this:
@GetMapping(path = "/retrieve-image",
produces = "image/png")
Additionally, instead of returning a Mono<byte[]>
, you can wrap your response in a ResponseEntity<Resource>
. This gives you the possibility to add Headers and tell the browser the name of your file. For example:
HttpHeaders header = new HttpHeaders();
header.add(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=image.png");
header.add("Access-Control-Expose-Headers", "Content-Disposition");
return ResponseEntity.ok().
.headers(header)
.contentLength(Files.size(path))
.body(<<YOUR_FILE_HERE>>);
One last thought: If you add both spring-boot-starter-web and spring-boot-starter-webflux to your dependencies, the app will work, but it doesn't use Netty from Webflux, instead the usual Tomcat. So you don't benefit from the reactive features.
Answered By - meberhard
Answer Checked By - Marilyn (JavaFixing Volunteer)