I have the following endpoint code to serve PDF files.
@RequestMapping
ResponseEntity<byte[]> getPDF() {
File file = ...;
byte[] contents = null;
try {
try (FileInputStream fis = new FileInputStream(file)) {
contents = new byte[(int) file.length()];
fis.read(contents);
}
} catch(Exception e) {
// error handling
}
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData(file.getName(), file.getName());
headeres.setCacheControl("must-revalidate, post-check=0, pre-check=0");
return new ResponseEntity<>(contents, headers, HttpStatus.OK);
}
How can I convert above into a reactive type Flux/Mono
and DataBuffer
.
I have check DataBufferUtils
but It doesn't seem to offer what I needed. I didn't find any example either.
From here it may look like Spring Integration flows are really a good fit for writing Reactive Streams applications when we apply some reactive framework operators on endpoints, but in fact the problems is much broader and we need to keep in mind that not all endpoints (e. g. JdbcMessageHandler) can be processed in a reactive stream transparently.
Reactive programming is non blocking, asynchronous and deals with data streams not single data and also supports backpressure. Spring supports reactive programming using “Project Reactor” , a platform which supports reactive programming.
This ReactiveMessageSourceProducercould be used for any use-case when a a polling channel adapter’s features should be turned into a reactive, on demand solution for any existing MessageSource<?>implementation. Splitter and Aggregator
By extending from the Spring CrudRepository, we will have some methods for our data repository implemented, including findAll () and findOne (). This way we do not have to write a lot of boilerplate code.
The easiest way to achieve that would be with a Resource
.
@GetMapping(path = "/pdf", produces = "application/pdf")
ResponseEntity<Resource> getPDF() {
Resource pdfFile = ...;
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData(file.getName(), file.getName());
return ResponseEntity
.ok().cacheControl(CacheControl.noCache())
.headers(headers).body(resource);
}
Note that DataBufferUtils
has some useful methods there that convert an InputStream
to a Flux<DataBuffer>
, like DataBufferUtils#read()
. But dealing with a Resource
is still superior.
Below is the code to return the attachment as byte stream:
@GetMapping(
path = "api/v1/attachment",
produces = APPLICATION_OCTET_STREAM_VALUE
)
public Mono<byte[]> getAttachment(String url) {
return rest.get()
.uri(url)
.exchange()
.flatMap(response -> response.toEntity(byte[].class));
}
This approach is very simple but the disadvantage is it will the load the entire attachment into memory. If the file size is larger, then it will be a problem.
To overcome we can use DataBuffer
which will send the data in chunks. This is an efficient solution and it will work for any large size file. Below is the modified code using DataBuffer:
@GetMapping(
path = "api/v1/attachment",
produces = APPLICATION_OCTET_STREAM_VALUE
)
public Flux<DataBuffer> getAttachment(String url) {
return rest.get()
.uri(url)
.exchange()
.flatMapMany(response -> response.toEntity(DataBuffer.class));
}
In this way, we can send attachments in a reactive fashion.
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