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

调用返回PDF的外部API时出现stream closed及格式不可接受错误

问题解决:外部API PDF流转发时的"stream is closed"与"Could not find acceptable representation"错误

问题背景

调用返回PDF格式ResponseEntity的外部API,需在自有端点提供该PDF文件,但当前出现两个关键错误:

  • stream is closed:流已关闭,无法读取PDF内容
  • Could not find acceptable representation:无法找到可接受的响应表示

相关代码如下:

原外部API FeignClient类

@FeignClient(value = "esatCalculoFeignClient", url = "${api.url.esat}")
public interface EsatCalculoFeignClient {

    @PostMapping("/pedido/gerar")
    ResponseEntity<InputStreamResource> gerarPdfDamEsat(@Validated @RequestBody EsatCalculoTaxasRequestDto dto);

}

原服务方法

// geração de DAM
public InputStreamResource solicitaDamEsat(SolicitacaDamRequestDto dto) throws BusinessException, IOException {
    FormSolicitacaoAlvara entity = this.buscaEntidadePorProtocolo(dto.getProtocolo());
    EsatCalculoTaxasRequestDto esatRequestDamDto = EsatCalculoTaxasRequestDto.toEsatDto(entity,dto.getIdMeioDePagamento(), dto.getQtdParcelas(), "123teste");
    ResponseEntity<InputStreamResource> file = esatCalculoFeignClient
            .gerarPdfDamEsat(esatRequestDamDto);

        InputStreamResource isr = new InputStreamResource(file.getBody().getInputStream());
        return isr;

 
}

原端点代码

@RequestMapping(value = "/imprimir",method = RequestMethod.GET, produces = "application/pdf")
public ResponseEntity<InputStreamResource> gerarDam(SolicitacaDamRequestDto dto) throws BusinessException, IOException {
    InputStreamResource file = service.solicitaDamEsat(dto);
    
    return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType("application/pdf"))
            .contentLength(file.contentLength())
            .header(HttpHeaders.CONTENT_DISPOSITION,"attachment: filename="+file.getFilename())
            .body(file);
}

错误原因分析

  1. 流自动关闭:Feign在处理完响应后会自动关闭InputStreamResource关联的输入流,服务方法中重新包装的InputStreamResource在到达端点时,流已被关闭,无法读取内容。
  2. 资源信息无法正确获取:InputStreamResource默认无法正确返回contentLength()和getFilename()的有效值,导致响应头信息错误,触发"Could not find acceptable representation"。
  3. 响应头格式错误:CONTENT_DISPOSITION头使用了冒号分隔,正确格式应为分号分隔(attachment; filename=xxx.pdf)。

修复方案

1. 修改FeignClient,返回byte[]替代InputStreamResource

直接获取响应体的字节数组,避免流关闭问题,Feign会自动完成转换:

@FeignClient(value = "esatCalculoFeignClient", url = "${api.url.esat}")
public interface EsatCalculoFeignClient {

    @PostMapping("/pedido/gerar")
    ResponseEntity<byte[]> gerarPdfDamEsat(@Validated @RequestBody EsatCalculoTaxasRequestDto dto);

}

2. 修改服务方法,返回ByteArrayResource

ByteArrayResource基于内存中的字节数组,不会有流关闭问题,且Spring能正确处理其资源信息:

// geração de DAM
public ByteArrayResource solicitaDamEsat(SolicitacaDamRequestDto dto) throws BusinessException {
    FormSolicitacaoAlvara entity = this.buscaEntidadePorProtocolo(dto.getProtocolo());
    EsatCalculoTaxasRequestDto esatRequestDamDto = EsatCalculoTaxasRequestDto.toEsatDto(entity, dto.getIdMeioDePagamento(), dto.getQtdParcelas(), "123teste");
    ResponseEntity<byte[]> fileResponse = esatCalculoFeignClient.gerarPdfDamEsat(esatRequestDamDto);
    
    return new ByteArrayResource(fileResponse.getBody());
}

3. 修改端点代码,修正响应头与资源处理

使用Spring常量规范响应类型,修正CONTENT_DISPOSITION格式,并移除不必要的异常声明:

@RequestMapping(value = "/imprimir", method = RequestMethod.GET, produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<ByteArrayResource> gerarDam(SolicitacaDamRequestDto dto) throws BusinessException {
    ByteArrayResource file = service.solicitaDamEsat(dto);
    
    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_PDF)
            .contentLength(file.contentLength())
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=dam.pdf")
            .body(file);
}

修复说明

  • 改用byte[]获取外部API响应:避免流生命周期管理问题,直接操作内存中的字节数据更可靠。
  • 使用ByteArrayResource:Spring对该类型的资源支持更完善,能正确返回内容长度,适配响应处理逻辑。
  • 修正响应头格式:符合HTTP规范,确保浏览器能正确识别附件文件名。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:55:33