Reputation: 5683
I'm trying to implement a binary content controller in Spring MVC.
It's working okay but I want to add caching control headers to the response.
I checked this related question: Unable to cache images served by Spring MVC
But it's using a different method. I wanted to use this requestMapping - produces annotation. Here's what I have so far, but I'm not sure how to set the response headers with the cache control elements.
@RequestMapping(value="/binaries/**", method = RequestMethod.GET, produces={MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_VALUE, MediaType.IMAGE_GIF_VALUE,
MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE})
public @ResponseBody byte[] serveResource(WebRequest webRequest, String uri) throws IOException {
String path = (String)request.getAttribute( HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE );
BinaryFile bf = binaryService.findByUri(path.replaceFirst("/binaries", ""));
if (webRequest.checkNotModified(bf.getLastModifiedDate().toDate().getTime()))
{
return null;
};
return bf.getResource();
}
Upvotes: 2
Views: 5061
Reputation: 5683
So I just reimplemented the resource handling and thought I'd post the answer back to this question.
Basically I used the new ResourceHandling framework, and it was surprisingly easy to implement this.
Created a BinaryImage class that's backed by a CLOB in the DB. Implement the Resource
interface from Spring 4.1+.
Then I created a new ResourceResolver
that looks up the incoming requests against a known path "/images".
Then configured it in the WebMVC framework.
Code for 2 & 3 follows (1 is ommitted, it's straightforward to implement the interface):
/**
* The Frontend Binary Controller
*/
@Component
public class BinaryResourceResolver implements ResourceResolver{
private static Logger logger = LogManager.getLogger(BinaryResourceResolver.class.getName());
@Autowired
HttpServletRequest request;
@Autowired
BinaryImageService binaryService;
@Override
public Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
if (!requestPath.startsWith("/"))
{
requestPath = "/" + requestPath;
}
Resource bf = binaryService.findByUrl(requestPath);
return bf;
}
@Override
public String resolveUrlPath(String resourcePath, List<? extends Resource> locations, ResourceResolverChain chain) {
return null;
}
}
Then the configuration code: is in a config class that extends WebMVCConfigurerAdapter
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations(new String[]{"/resources/", "/products/", "/categories/"})
.setCachePeriod(30);
registry.addResourceHandler("/assets/**").addResourceLocations(new String[]{"/assets/"}).setCachePeriod(3600);
registry.addResourceHandler("/frontend/**").addResourceLocations(new String[]{"/frontend/"}).setCachePeriod(3600);
registry.addResourceHandler("/images/**").setCachePeriod(3600).resourceChain(true).addResolver(binaryResourceResolver);
registry.setOrder(-1);
}
Upvotes: 2
Reputation: 29
You should provide last modified header while returning the byte array
if (webRequest.checkNotModified(bf.getLastModifiedDate().toDate().getTime()))
{
return null;
};
SimpleDateFormat dateFormat=new SimpleDateFormat();
response.addHeader("Last-Modified", dateFormat.format(bf.getLastModifiedDate().toDate().getTime()));
return bf.getResource();
Upvotes: 0
Reputation: 59086
You could use ResponseEntity
like this:
@ResponseBody
public ResponseEntity<byte[]> serveResource() {
//..
return ResponseEntity.ok()
.lastModified(lastModified)
.body(bf);
}
Or serve resources directly with Spring Resource Handling support.
Note that multiple HTTP caching improvements are scheduled for Spring Framework 4.2, now is the time to voice your opinion on this (you can comment/votes for issues).
Upvotes: 2
Reputation: 66
How about this:
public @ResponseBody byte[] serveResource(WebRequest webRequest, HttpServletResponse response, String uri) throws IOException {
response.addHeader("Cache-Control", "private, max-age=0, no-cache");
// ...
Upvotes: 2