Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot FeignClient with Image response give error 'stream is closed'

My Spring REST controller which uses FeignClient for consuming another Image/Blob storage microservice fails with error 'stream is closed'. whats the proper way to consume blob data service.

REST Controller

@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 interface

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

Error when invoking the microservice /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]
like image 850
Gireesh Avatar asked Jul 27 '16 15:07

Gireesh


1 Answers

If I use ResponseEntity with byte[] as response, everything is working perfectly:

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

But it may cause OutOfMemoryError, so I've made an investigation and found better solution:

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

And I'm working with blobService in a such way:

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

Also I found in Feign sources that response.body().asInputStream() returns original sun.net.www.http.KeepAliveStream if response size is over than 8192 bytes

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

Otherwise feign.Response.ByteArrayBody will be created. And if you try to access InputStream it will return ByteArrayInputStream. That all stuff solves possible OutOfMemoryError.

like image 100
Nazar Avatar answered Sep 30 '22 18:09

Nazar