调用返回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); }
错误原因分析
- 流自动关闭:Feign在处理完响应后会自动关闭
InputStreamResource关联的输入流,服务方法中重新包装的InputStreamResource在到达端点时,流已被关闭,无法读取内容。 - 资源信息无法正确获取:
InputStreamResource默认无法正确返回contentLength()和getFilename()的有效值,导致响应头信息错误,触发"Could not find acceptable representation"。 - 响应头格式错误:
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
相关产品推荐
相关产品推荐

