Webflux环境下如何正确将InputStreamResource封装到ResponseEntity中
问题根源
你的代码核心问题是打破了Reactive流的执行链条:crossRefService返回的是Reactive Publisher(Mono/Flux),你在Service内部手动订阅后直接返回未就绪的InputStreamResource,Controller将其包装进Mono.just()返回时,Spring WebFlux会认为响应已经准备完成,直接读取还未填充数据的输入流返回,此时内部的交叉引用查询、PDF拉取逻辑还在异步线程中未执行,就出现了你遇到的问题。
正确修改方案
推荐你不需要手动转换DataBuffer为InputStreamResource,Spring WebFlux原生支持直接返回Flux<DataBuffer>作为响应体,简化代码同时避免管道流处理的额外问题:
修改后的PdfService.groovy
@Service class PdfService { @Autowired WebClient webClient @Autowired CrossRefService crossRefService // 返回Flux<DataBuffer>,保持完整的Reactive链条 Flux<DataBuffer> getPdf(String userId, String pdfId) { return crossRefService .getCrossRefId(userId) .flatMapMany(crossRefResponse -> webClient .get() .uri("https://some-url/${pdfId}.pdf", { it.queryParam("crossRefId", crossRefResponse.id) it.build() }) .accept(MediaType.APPLICATION_PDF) .retrieve() .bodyToFlux(DataBuffer) ) } }
修改后的PdfController.groovy
@RestController @RequestMapping("/pdf/{pdfId}.pdf") class PdfController { @Autowired PdfService service @GetMapping Mono<ResponseEntity<Flux<DataBuffer>>> getPdf(@AuthenticationPrincipal Jwt jwt, @PathVariable String pdfId) { return service.getPdf(jwt.claims.userId, pdfId) .map(flux -> ResponseEntity.ok() .contentType(MediaType.APPLICATION_PDF) .body(flux)) } }
问题解答
- 将InputStreamResource放入Publisher的正确方式:需要将交叉引用查询、PDF拉取、流转换的所有逻辑都封装到Publisher链条内,由Spring负责订阅触发,不能先执行异步逻辑再把结果包装进
Mono.just()。如果一定要使用InputStreamResource,可以用以下方式实现:
Mono<InputStreamResource> getPdf(String userId, String pdfId) { return crossRefService .getCrossRefId(userId) .flatMapMany(crossRefResponse -> webClient .get() .uri("https://some-url/${pdfId}.pdf", { it.queryParam("crossRefId", crossRefResponse.id) .build() }) .accept(MediaType.APPLICATION_PDF) .retrieve() .bodyToFlux(DataBuffer) ) // 把流转换逻辑放到Reactive链条内 .map(this::getInputStreamFromFluxDataBuffer) }
- 这步操作是必须的:Spring WebFlux的请求处理基于Reactive流的订阅触发机制,只有你返回的Publisher被Spring订阅时,内部的异步逻辑才会按顺序执行。如果直接返回
Mono.just()包装的预创建对象,Spring会直接读取对象值返回,不会等待你内部手动订阅的异步逻辑执行完成,会导致响应异常。
内容的提问来源于stack exchange,提问作者jndietz
相关产品推荐
相关产品推荐

