【问题标题】:Spring Boot FeignClient with Image response give error 'stream is closed'带有图像响应的 Spring Boot FeignClient 给出错误“流已关闭”
【发布时间】:2016-12-01 16:35:29
【问题描述】:

我的 Spring REST 控制器使用 FeignClient 使用另一个 Image/Blob 存储微服务失败,并出现错误“流已关闭”。使用 blob 数据服务的正确方法是什么。

REST 控制器

@RestController
@RequestMapping("/blob")
public class BlobDataRestController  extends BaseRESTController{

    @Autowired
    private IBlobService blobService;

    @CrossOrigin
    @RequestMapping(value = "/{filename:.+}", method = RequestMethod.GET)
     public ResponseEntity<InputStreamResource> getFile(@PathVariable(value = "filename") String filename){
        return blobService.getFile(filename);
    }
}

FeignClient 接口

@FeignClient(name="blob-service", url="${blob-service.url}")
public interface IBlobService {

     @RequestMapping(value = "/blob/{filename:.+}", method = RequestMethod.GET)
     public ResponseEntity<InputStreamResource> getFile(@PathVariable(value = "filename") String filename);
}

调用微服务时出错/blob/filename.jpg

2016-07-27T20:31:41.73+0530 [App/0]      OUT 2016-07-27 15:01:41.732 ERROR 29 --- [io-61779-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
2016-07-27T20:31:41.73+0530 [App/0]      OUT java.io.IOException: stream is closed
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.ensureOpen(HttpURLConnection.java:3309) ~[na:1.8.0_91]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3334) ~[na:1.8.0_91]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at java.io.FilterInputStream.read(FilterInputStream.java:133) ~[na:1.8.0_91]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at java.io.PushbackInputStream.read(PushbackInputStream.java:186) ~[na:1.8.0_91]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at java.io.FilterInputStream.read(FilterInputStream.java:107) ~[na:1.8.0_91]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.util.StreamUtils.copy(StreamUtils.java:126) ~[spring-core-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.http.converter.ResourceHttpMessageConverter.writeInternal(ResourceHttpMessageConverter.java:102) ~[spring-web-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.http.converter.ResourceHttpMessageConverter.writeInternal(ResourceHttpMessageConverter.java:47) ~[spring-web-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:195) ~[spring-web-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:238) ~[spring-webmvc-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183) ~[spring-webmvc-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) ~[spring-web-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126) ~[spring-webmvc-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) ~[spring-webmvc-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) ~[spring-webmvc-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) ~[spring-webmvc-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) ~[spring-webmvc-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]
2016-07-27T20:31:41.73+0530 [App/0]      OUT    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.2.6.RELEASE.jar!/:4.2.6.RELEASE]

【问题讨论】:

  • 您是否尝试将其检索为byte[]?保持打开流是在任何类型的负载下都可能变得棘手的事情。
  • 是的,我尝试了返回 byte[],但没有成功。没有尝试在磁盘上保存临时文件,不想在返回图像时增加额外的延迟

标签: java spring rest spring-boot


【解决方案1】:

如果我使用 ResponseEntity 和 byte[] 作为响应,一切都运行良好:

@FeignClient(name="blob-service", url="${blob-service.url}")
public interface IBlobService {
     @RequestMapping(value = "/blob/{filename:.+}", method = RequestMethod.GET)
     public ResponseEntity<byte[]> getFile(@PathVariable(value = "filename") String filename);
}

但它可能会导致OutOfMemoryError,所以我进行了调查并找到了更好的解决方案:

@FeignClient(name="blob-service", url="${blob-service.url}")
public interface IBlobService {
     @RequestMapping(value = "/blob/{filename:.+}", method = RequestMethod.GET)
     public feign.Response getFile(@PathVariable(value = "filename") String filename);
}

我正在以这种方式使用 blobService:

try (Response response = blobService.getFile(fileName)) {
    InputStream is = response.body().asInputStream();
  // work with is
}

我还在Feign sources 中发现,如果响应大小超过 8192 字节,response.body().asInputStream() 会返回原始 sun.net.www.http.KeepAliveStream

if (response.body().length() == null ||
            response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
    shouldClose = false;
    return response;
}

否则会创建 feign.Response.ByteArrayBody。如果您尝试访问 InputStream it will return ByteArrayInputStream。 所有东西都解决了可能的 OutOfMemoryError。

【讨论】:

    猜你喜欢
    • 2017-08-31
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    • 2015-06-19
    • 2020-03-26
    • 1970-01-01
    • 2016-06-13
    • 1970-01-01
    相关资源
    最近更新 更多