Struts2中图片设置缓存

在Struts2中,如果图片用InputStream 字节输入流进行读取图片,浏览器加载页面时每次都会重新加载图片,这样不但加重了服务器负担,还增加了带宽流量,并且图片本来是比较大的资源,会大大增加带宽资费。
所以为了开源节流,为Struts2中图片设置浏览器缓存是必须的。

首先,先了解几个概念

HTTP/304
这个是一种服务器返回的响应类型,如果服务器返回304状态码,那么浏览器会从自己的缓存里读取相应的资源。而非304的正常响应是200,意思是服务器重新返回资源,浏览器会用新的资源覆盖旧的缓存。

Last-Modified:
浏览器在请求数据后,服务器返回的数据中,如果带有Last-Modified属性,Chrome中显示为
Last-Modified: Wed, 15 Dec 2010 05:56:21 GMT
那么,浏览器会在本次请求的资源中一起记录这个时间,并且用此时间来计算资源过期时间,未过期时,浏览器再次加载相同的资源时直接从缓存里读取而不请求。缓存的时间可用Expires来控制。

If-Modified-Since:
服务器在响应请求的头部带有Last-Modified属性,并且浏览器对此资源已经缓存,那么,浏览器在判断到此资源过期后(未过期不请求),会再次请求此资源,并在请求头部带有If-Modified-Since属性,这个属性的值是上次服务器响应头中的Last-Modified属性。服务器可通过此属性判断浏览器中缓存资源的版本,并作出相应处理,返回200状态的资源或者304。

Etag/If-None-Match:
与Last-Modiefied和If-Modified-Since类似,不同为:这两个属性值不是时间,而是一个字符串,第一次请求时,服务器要返回一个带有Etag属性的响应,下次浏览器请求时会提供上次服务器返回的Etag值(此时属性名为If-None-Match),服务器用这个字符串去判断资源是否过期。但是,这个属性在浏览器中没有是否过期的概念,所以浏览器不会直接读取缓存,而一定会先向服务器请求,再根据浏览器返回值判断是否读取缓存。

了解了这样概念之后,就可以写相应逻辑了。当然,缓存的设置不仅仅能用于图片,也包括其他资源,此处只针对图片讲解。

在Struts2中返回图片,在result中用的是Stream类型,额,这个不重要,什么类型不影响。
因为在Action中,我们要返回Last-Modified或者Etag,要得到HttpServletResponse对象,获取If-Modified-Since/If-None-Match要先得到HttpServletRequest对象。

  • 通过HttpServletRequest.getDateHeader(“If-Modified-Since”)得到If-Moditied-Since,类型为long
  • 通过HttpServletRequest.getHeader(“If-None-Match”)得到If-None-Match,类型为String
  • 通过HttpServletResponse.addDateHeader(“Last-Modified”, ****)或者HttpServletResponse.setDateHeader(“Last-Modified”, ****)设置Last-Modified
  • 通过HttpServletResponse.addHeader(“Etag”, ****)或者HttpServletResponse.setHeader(“”, ****)设置Etag

因为getDateHeader方法得到的是long值的时间,可用Date.getTime()得到时间用来做对比。

具体逻辑:

struts2缓存

当浏览器第一次请求时,不会有If-Modified-Since属性,可通过HttpServletRequest.containsHeader(String name)判断是否有此属性,不存在时,在response中设置Last-Modified资源修改时间。并返回资源。
如果请求中带有If-Modified-Since,并判断是否过期,过期重新设置Last-Modified的值并回返资源。
如果未过期,则返回一个type为httpheader的result,如下,在Action中return “notmodified”即可。

 

注意:如果设置了Last-Modified的值,浏览器可能会直接读取缓存而不向服务器请求,加载的资源有可能是过期的,服务器更新后浏览器仍显示过期资源,如果需要及时请求,请设置Cache-Control: max-age=或 Expires属性,也可用Etag/If-None-Match。但这个只影响点击或通过地址栏进入网页,对于刷新按钮,所有资源都会向服务器请求。

 

完整示例代码:

 

 

发表评论