You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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))
    }
}

问题解答

  1. 将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)
}
  1. 这步操作是必须的:Spring WebFlux的请求处理基于Reactive流的订阅触发机制,只有你返回的Publisher被Spring订阅时,内部的异步逻辑才会按顺序执行。如果直接返回Mono.just()包装的预创建对象,Spring会直接读取对象值返回,不会等待你内部手动订阅的异步逻辑执行完成,会导致响应异常。

内容的提问来源于stack exchange,提问作者jndietz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 08:45:02