我有以下端点代码来提供PDF文件。
@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);
}
如何将上述内容转换为反应式Flux/Mono
和DataBuffer
。
我检查了DataBufferUtils
,但它似乎没有提供我需要的东西。我也没有找到任何例子。
答案 0 :(得分:5)
实现这一目标的最简单方法是使用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);
}
请注意,DataBufferUtils
有一些有用的方法可以将InputStream
转换为Flux<DataBuffer>
,例如DataBufferUtils#read()
。但处理Resource
仍然是优越的。
答案 1 :(得分:0)
我遇到相同的问题。
我使用Webflux Spring WebClient
我写风格RouterFunction
我在下面的解决方案
ETaxServiceClient.java
final WebClient defaultWebClient;
public Mono<byte[]> eTaxPdf(String id) {
return defaultWebClient
.get()
.uri("-- URL PDF File --")
.accept(MediaType.APPLICATION_OCTET_STREAM)
.exchange()
.log("eTaxPdf -> call other service")
.flatMap(response -> response.toEntity(byte[].class))
.flatMap(responseEntity -> Mono.just(Objects.requireNonNull(responseEntity.getBody())));
}
ETaxHandle.java
@NotNull
public Mono<ServerResponse> eTaxPdf(ServerRequest sr) {
Consumer<HttpHeaders> headers = httpHeaders -> {
httpHeaders.setCacheControl(CacheControl.noCache());
httpHeaders.setContentDisposition(
ContentDisposition.builder("inline")
.filename(sr.pathVariable("id") + ".pdf")
.build()
);
};
return successPDF(eTaxServiceClient
.eTaxPdf(sr.pathVariable("id"))
.switchIfEmpty(Mono.empty()), headers);
}
ETaxRouter.java
@Bean
public RouterFunction<ServerResponse> routerFunctionV1(ETaxHandle handler) {
return route()
.path("/api/v1/e-tax-invoices", builder -> builder
.GET("/{id}", handler::eTaxPdf)
)
.build();
}
CommonHandler.java
Mono<ServerResponse> successPDF(Mono<?> mono, Consumer<HttpHeaders> headers) {
return ServerResponse.ok()
.headers(headers)
.contentType(APPLICATION_PDF)
.body(mono.map(m -> m)
.subscribeOn(Schedulers.elastic()), byte[].class);
}
结果:已成功显示在浏览器中。
为我工作。
答案 2 :(得分:-1)
下面是将附件作为字节流返回的代码:
@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));
}
这种方法非常简单,但是缺点是它将整个附件加载到内存中。如果文件较大,将是一个问题。
要克服这一点,我们可以使用DataBuffer
来分块发送数据。这是一种有效的解决方案,适用于任何大文件。下面是使用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));
}
通过这种方式,我们可以以响应方式发送附件。