Richard G
Richard G

Reputation: 5683

how to add cache control headers to image served by spring mvc

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

Answers (4)

Richard G
Richard G

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.

  1. Created a BinaryImage class that's backed by a CLOB in the DB. Implement the Resource interface from Spring 4.1+.

  2. Then I created a new ResourceResolver that looks up the incoming requests against a known path "/images".

  3. 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

SUNIL SP
SUNIL SP

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

Brian Clozel
Brian Clozel

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

Chris
Chris

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

Related Questions