如何以响应式方式通过 Mono 返回应用程序/pdf
Posted
技术标签:
【中文标题】如何以响应式方式通过 Mono 返回应用程序/pdf【英文标题】:How to return application/pdf through Mono in a Reactive way 【发布时间】:2021-10-11 23:25:35 【问题描述】:我目前正在使用 Spring WebFlux 尝试构建一个异步端点,该端点通过 Web 客户端从第三方端点获取 PDF,然后将 PDF 返回给我们的 API 使用者。但是,由于以下异常,我正在努力返回内容类型为application/pdf
的Mono<ResponseEntity>
:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class reactor.core.publisher.MonoMapFuseable] with preset Content-Type 'application/pdf']
这里是控制器的实现。我的问题是:
我的实现方向是否正确,还是需要创建某种转换器?Mono<ResponseEntity>
是否甚至支持返回 PDF 作为响应正文?
@RequestMapping(value="/get-pdf", method = RequestMethod.GET)
public Mono<ResponseEntity> getPDFAsync()
String url = "http://some-end-point";
WebClient client = WebClient.create(url);
return client.get()
.accept(MediaType.APPLICATION_PDF)
.exchangeToMono(response ->
Mono.just(ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF)
.body(response.bodyToMono(ByteArrayResource.class)
.map(byteArrayResource -> byteArrayResource.getByteArray())
)));
【问题讨论】:
这能回答你的问题吗? Return generated pdf using spring MVC @Toerktumlare 这不是一种非常被动的方法,请参阅我的答案以获得一种可能的解决方案。 我并没有声称它是一个完整的服务答案,但它为您提供了如何完成的指南,然后您可以将其调整为响应式。例如,您在我发布的链接中查看的第二个问题,它们传输字节数组,而 webflux 支持传输字节流。 【参考方案1】:要响应式下载文件,您可以将文件作为Flux<DataBuffer>
提供,其中DataBuffer
是org.springframework.core.io.buffer.DataBuffer
,如下所示:
// some shared buffer factory.
private final DataBufferFactory dataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
@RequestMapping(value = "/download",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_PDF_VALUE
)
public Mono<ResponseEntity<Flux<DataBuffer>>> downloadDocument(
...
)
return Mono.fromCallable(() ->
return ResponseEntity.ok(
DataBufferUtils.read(
new File("somepdf.pdf").toPath(),
dataBufferFactory,
8096
))
);
或者更具体地说,由于您似乎在使用 WebFlux WebClient,因此您可以将响应主体通量直接转发到您自己的响应中,而不必先缓冲完整的响应:
@RequestMapping(value = "/download",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_PDF_VALUE
)
public Mono<ResponseEntity<Flux<DataBuffer>>> downloadDocument(
...
)
String url = "http://some-end-point";
WebClient client = WebClient.create(url);
return client.get()
.accept(MediaType.APPLICATION_PDF)
.exchange()
.map(response -> response.bodyToFlux(DataBuffer.class))
.map(ResponseEntity::ok);
提示:我希望您重用 WebClient 实例,而不是在每个请求上都实例化一个新实例。
【讨论】:
这仍然给出错误“无转换器”等。 @user3067860 你确定你有正确的 WebFlux 依赖项吗?这应该在任何 webflux 项目上开箱即用,但不适用于具有反应器依赖项的 spring MVC。我有一个带有反应器库的弹簧 MVC 并且必须编写自定义转换器的情况。如果您遇到这种情况,请随时发布新问题并告诉我,我会将转换器发送给您。【参考方案2】:我找到了答案!简而言之,返回Mono<byte[]>
,并将produces = MediaType.APPLICATION_PDF_VALUE
添加到@RequestMapping
有效。请参见下面的示例。
@RequestMapping(value="/get-pdf", produces = MediaType.APPLICATION_PDF_VALUE, method = RequestMethod.GET)
public Mono<byte[]> getPdf()
String url = "some-end-point";
WebClient client = WebClient.create(url);
return client.get()
.accept(MediaType.APPLICATION_PDF)
.exchangeToMono(response -> response
.bodyToMono(ByteArrayResource.class))
.map(byteArrayResource -> byteArrayResource.getByteArray());
【讨论】:
如果您的 PDF 变得稍微不小,这最终将停止工作。以上是关于如何以响应式方式通过 Mono 返回应用程序/pdf的主要内容,如果未能解决你的问题,请参考以下文章