1

我有一個servlet,返回的圖像爲InputStreamResource。根據某些get query參數將會返回大約50張靜態圖像。如何緩存一個InputStreamResource在RestController中?

爲了不必在每次請求時都查找這些圖像(通常是),我想要緩存這些圖像響應。

@RestController 
public class MyRestController { 
    //code is just example; may be any number of parameters 
    @RequestMapping("/{code}") 
    @Cachable("code.cache") 
    public ResponseEntity<InputStreamResource> getCodeLogo(@PathVariable("code") String code) { 
     FileSystemResource file = new FileSystemResource("d:/images/" + code + ".jpg"); 
     return ResponseEntity.ok() 
      .contentType("image/jpg") 
      .lastModified(file.lastModified()) 
      .contentLength(file.contentLength()) 
      .body(new InputStreamResource(file.getInputStream())); 

    } 
} 

當(如果直接無論在RestMapping方法或重構到外部服務)使用@Cacheable註釋,I_'m收到以下異常:

cause: java.lang.IllegalStateException: InputStream has already been read - do not use InputStreamResource if a stream needs to be read multiple times - error: InputStream has already been read - do not use InputStreamResource if a stream needs to be read multiple times 
org.springframework.core.io.InputStreamResource.getInputStream(InputStreamResource.java:96) 
org.springframework.http.converter.ResourceHttpMessageConverter.writeInternal(ResourceHttpMessageConverter.java:100) 
org.springframework.http.converter.ResourceHttpMessageConverter.writeInternal(ResourceHttpMessageConverter.java:47) 
org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:195) 
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:238) 
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183) 
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) 
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126) 
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) 
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) 

問題:怎麼能然後,我緩存ResponseEntity類型InputStreamResource

+0

是否需要只讀一次?還是應該監視磁盤上的文件以進行更改? – Niels

+0

我想在固定的時間段內,例如每晚或每週打電話給@ @ EvictCache。所以一般而言,它應該只准備一次,然後重新使用而不必在本地查找文件。 – membersound

+3

清除。否則,您可能在控制器初始化期間讀取一次該文件,並將其加載到ByteArrayResource中... – Niels

回答

2

緩存管理器將添加到緩存ResponseEntityInputStreamResource裏面。第一次它會好的。但是,當緩存ResponseEntity第二次嘗試讀取InputStreamResouce時,您將得到例外,因爲它無法讀取一次以上的數據流。

解決方法:不要緩存InputStreamResouce本身,而是緩存流的內容。

@RestController 
public class MyRestController {  
    @RequestMapping("/{code}") 
    @Cachable("code.cache") 
    public ResponseEntity<byte[]> getCodeLogo(@PathVariable("code") String code) { 
     FileSystemResource file = new FileSystemResource("d:/images/" + code + ".jpg"); 

     byte [] content = new byte[(int)file.contentLength()]; 
     IOUtils.read(file.getInputStream(), content); 

     return ResponseEntity.ok() 
      .contentType(MediaType.IMAGE_JPEG) 
      .lastModified(file.lastModified()) 
      .contentLength(file.contentLength()) 
      .body(content); 
    } 
} 

我用IOUtils.read()org.apache.commons.io,從流複製字節數組,但你可以通過任何首選的方式做到這一點。

1

您無法緩存數據流。一旦他們被閱讀,他們就消失了。 該錯誤消息是關於很清楚:

InputStream has already been read - 
do not use InputStreamResource if a stream needs to be read multiple times 

通過代碼和註釋,在我看來,你必須以JPG的標誌大images文件夾(可以添加,刪除或修改),並且你想要有一個每日緩存你正在被要求的人,所以你不必不斷地從磁盤重新加載它們。

如果是這樣的話,最好的選擇是將文件內容讀入ByteArray並緩存/返回。

+0

你的假設是完全正確的。 – membersound

相關問題