我有一个servlet,它返回一个InputStreamResource
的图像。根据一些get query
参数,大约有50个静态图像要返回。
因为每次请求时都不必查看每个图像(通常是这样),我想缓存这些图像的响应。
@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()));
}
}
使用@Cacheable
注释时(无论是直接在RestMapping
方法上还是重构为外部服务),我都会遇到以下异常:
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 :(得分:6)
缓存管理器将添加到缓存ResponseEntity
,其中包含InputStreamResource
。第一次没问题。但是,当缓存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 :(得分:2)
您无法缓存Streams。一旦阅读完毕,它们便消失了。 错误信息非常清楚:
InputStream has already been read -
do not use InputStreamResource if a stream needs to be read multiple times
根据您的代码和评论,在我看来,您有一个带有JPG徽标的大images
文件夹(可能会添加,删除或修改),并且您希望每天都有 你要求的人的缓存,所以你不必经常从磁盘重新加载它们。
如果是这种情况,您最好的选择是将文件的内容读取到ByteArray并缓存/返回。