如何在 RestController 中缓存 InputStreamResource?
How to cache a InputStreamResource In RestController?
我有一个 servlet,returns 图像为 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)
问题:我怎样才能完全缓存 InputStreamResource
类型的 ResponseEntity
?
缓存管理器会将 InputStreamResource
添加到缓存 ResponseEntity
中。第一次会没事的。但是当缓存 ResponseEntity
将尝试读取 InputStreamResouce
第二次时你会得到异常,因为它无法多次读取流。
解决方法:不缓存InputStreamResouce
本身,而是缓存stream的内容。
@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);
}
}
我使用了 org.apache.commons.io
中的 IOUtils.read()
来将字节从流复制到数组,但您可以通过任何首选方式来完成。
您无法缓存流。一旦他们被阅读,他们就消失了。
错误消息非常清楚:
InputStream has already been read -
do not use InputStreamResource if a stream needs to be read multiple times
根据你的代码和评论,在我看来你有一个带有 JPG 徽标的大 images
文件夹(可以添加、删除或修改),并且你想要一个 每天 缓存你被要求的那些,所以你不必经常从磁盘重新加载它们。
如果是这种情况,您最好的选择是将文件的内容读取到 ByteArray,然后 cache/return 改为读取文件内容。
我有一个 servlet,returns 图像为 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)
问题:我怎样才能完全缓存 InputStreamResource
类型的 ResponseEntity
?
缓存管理器会将 InputStreamResource
添加到缓存 ResponseEntity
中。第一次会没事的。但是当缓存 ResponseEntity
将尝试读取 InputStreamResouce
第二次时你会得到异常,因为它无法多次读取流。
解决方法:不缓存InputStreamResouce
本身,而是缓存stream的内容。
@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);
}
}
我使用了 org.apache.commons.io
中的 IOUtils.read()
来将字节从流复制到数组,但您可以通过任何首选方式来完成。
您无法缓存流。一旦他们被阅读,他们就消失了。 错误消息非常清楚:
InputStream has already been read -
do not use InputStreamResource if a stream needs to be read multiple times
根据你的代码和评论,在我看来你有一个带有 JPG 徽标的大 images
文件夹(可以添加、删除或修改),并且你想要一个 每天 缓存你被要求的那些,所以你不必经常从磁盘重新加载它们。
如果是这种情况,您最好的选择是将文件的内容读取到 ByteArray,然后 cache/return 改为读取文件内容。