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]
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With