为组件添加Expires头,最大化利用浏览器缓存

web项目一旦部署完毕,项目中的图片、CSS以及JS基本上很少发生变动,那么假如把这些组件缓存在浏览器客户端,而不再从服务器上获取,那么网站的访问者在首次访问网站后,后续的请求将会大量减轻服务器的请求压力。这一举动,带来的性能提升,可以称作完美!那么如何做呢?那就是为组件添加Expires(期限)头!

一、了解Expires头

起初,在读《高性能web建站指南》第三章“添加Expires头”时,感觉很有压力,因为不了解Expires头是做什么用的,所以就没有心思去实践该做法,就搁置了一段时间。然而今天心情很好,耐着性子在网上找一系列的资源,并且重读这篇文章,实践再三,终于搞定,特此分享,希望更多的朋友能够看到,实践到你的项目中,从而提升网站性能。

我提供几篇文章大家读一读:

  1. tomcat7官方doc中给出的Expires_Filter,可结合源码进行分析。
  2. 浏览器缓存详细解析
  3. HTTP1.1 协议
  4. 网站性能优化:cache-control设置详解

有了这几篇文章作为铺垫,我想你基本上就可以了解Expires头啦。

二、效果

对于一个添加了Expires头的css来说,其请求的信息如图,多了max-age,以及Expires,这两者之间的关系我也不再赘述,之前的文章中描述比较清楚。

三、实现方法

①、新建CacheControlFilter.java

package com.honzh.common.filter;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class CacheControlFilter implements Filter {
    private FilterConfig config = null;

    private static final String CACHE_CONTROL_BY_TYPE = "Cache-Control";
    private static final String HEADER_EXPIRES = "Expires";

    private static final Log log = LogFactory.getLog(CacheControlFilter.class);

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {

        if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
             HttpServletRequest httpRequest = (HttpServletRequest) request;

            boolean cacheControlSet = false;
            for (Enumeration<String> names = config.getInitParameterNames(); names.hasMoreElements();) {
                String headerName = (String) names.nextElement();
                log.debug("参数名称:" + headerName);
                String value = config.getInitParameter(headerName);
                log.debug("参数值:" + value);

                // 说明包含了类型,此时要筛选符合条件的类型
                String type = headerName.substring(CACHE_CONTROL_BY_TYPE.length()).trim();
                log.debug("参数规定的类型:" + type);

                String contentType = httpRequest.getHeader("Accept");
                log.debug("请求内容类型为:" + contentType);

                if (contains(contentType, ";")) {
                    // lookup content-type without charset match (e.g.
                    // "text/html")
                    String contentTypeWithoutCharset = substringBefore(contentType, ";").trim();

                    if (contentTypeWithoutCharset.indexOf(type) != -1) {
                        // 类型匹配,再看值是不是max-age,如果是,将max-age设置为当前时间+1个月
                        Calendar calendar = Calendar.getInstance();
                        calendar.setTime(new Date());
                        calendar.add(Calendar.MONTH, 1);
                        Date expirationDate = calendar.getTime();

                        httpResponse.setDateHeader(HEADER_EXPIRES, expirationDate.getTime());

                        String maxAgeDirective = "max-age="
                                + ((expirationDate.getTime() - System.currentTimeMillis()) / 1000);

                        setControlHeader(httpResponse, "private," + maxAgeDirective);

                        cacheControlSet = true;
                    }
                }
            }
            if (!cacheControlSet) {
                setControlHeader(httpResponse, "private");
            }

        }

        chain.doFilter(request, response);

        // resp.setHeader("Expires", "Tue, 03 Jul 2001 06:00:00 GMT");
        // resp.setDateHeader("Last-Modified", new Date().getTime());
        // resp.setHeader("Cache-Control",
        // "no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
        // resp.setHeader("Cache-Control", "private");
        // resp.setHeader("Pragma", "no-cache");
    }

    private void setControlHeader(HttpServletResponse httpResponse, String cache_control) {
        String cacheControlHeader = httpResponse.getHeader(CACHE_CONTROL_BY_TYPE);
        String newCacheControlHeader = (cacheControlHeader == null) ? cache_control : cacheControlHeader + ", "
                + cache_control;
        httpResponse.setHeader(CACHE_CONTROL_BY_TYPE, newCacheControlHeader);
    }

    @Override
    public void destroy() {

    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        this.config = config;
    }

    protected static boolean contains(String str, String searchStr) {
        if (str == null || searchStr == null) {
            return false;
        }
        return str.indexOf(searchStr) >= 0;
    }

    protected static String substringBefore(String str, String separator) {
        if (str == null || str.isEmpty() || separator == null) {
            return null;
        }

        if (separator.isEmpty()) {
            return "";
        }

        int separatorIndex = str.indexOf(separator);
        if (separatorIndex == -1) {
            return str;
        }
        return str.substring(0, separatorIndex);
    }
}
  1. web.xml中我们为Expires设置了三种类型,分别为image、css、js,②中有详细参数,这样在filter中,我们将为这三种类型添加Expires、max-age。
  2. 至于类中提到的private,你可以关注我的另一篇文章gzip压缩tomcat服务器响应包,大幅提升web性能
  3. 其他方面,代码不复杂,主要的是做法,我将做法提供给大家。

②、web.xml

<filter>
        <filter-name>cacheControlFilter</filter-name>
        <filter-class>com.honzh.common.filter.CacheControlFilter</filter-class>
        <init-param>
            <param-name>Cache-Control image</param-name>
            <param-value>max-age</param-value>
        </init-param>
        <init-param>
            <param-name>Cache-Control text/css</param-name>
            <param-value>max-age</param-value>
        </init-param>
        <init-param>
            <param-name>Cache-Control application/javascript</param-name>
            <param-value>max-age</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>cacheControlFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


以上,两个步骤(为组件添加Expires头)完成后,你的项目就又在性能上有了进一步的提升,感觉怎么样呢?

时间: 2024-08-10 02:11:39

为组件添加Expires头,最大化利用浏览器缓存的相关文章

前端性能优化 —— 添加Expires头

简要:添加Expires头能有效的利用浏览器的缓存能力来改善页面的性能,能在后续的页面中有效避免很多不必要的Http请求,WEB服务器使用Expires头来告诉Web客户端它可以使用一个组件的当前副本,直到指定的时间为止. 例如:Expires:Thu,15 Apr  2010  20:00:00  GMT;  他告诉浏览器缓存有效性持续到2010年4月15日为止,在这个时间之内相同的请求使用缓存,这个时间之外使用http请求. Cathe-Control:max-age=315360000 E

IIS添加Expires头

今天的Web页面包含大量组件,并且数量不断增长:页面的初访问者会进行很多HTTP请求,但通过使用一个长久的Expires头,使这些组件可以被缓存.这会在后续的页面浏览中避免不必要的HTTP请求.长久的Expires头最常用于图片,但应该将其用在所有组件上,包括脚本.样式表和Flash. 打开IIS. 选中站点,在功能视图中找到“HTTP响应头”,打开功能. 设置常用标头. 勾选“保持HTTP连接”,勾选“使Web内容过期”,勾选“时间(协调通用时间(UTC))”. 如此,我们可在站点WebCon

利用浏览器缓存抓取网络资源:【炉石传说】所有卡牌png图片地址

有的时候我们在网络上看到很好的资源,想要抓取,却苦于没有好的手段.今天我就来介绍一下通过chrome浏览器缓存抓取的方法. 大家都知道,浏览器把网页展示给我们看的时候,会先将页面上的所有图片等资源下载到浏览器缓存中,利用这一点,我们就可以抓取了. 我选取的是当下比较流行的卡牌游戏[炉石传说],其游戏性的重要组成部分就是千奇百怪的卡牌. 首先,我们进入官网中的卡牌工具页面(http://hs.blizzard.cn/cards/builder/),在这里我们可以通过查找,找到每一张炉石卡牌.我们当

HTTP If-Modified-Since引发的浏览器缓存汇总

在看Spring中HttpServlet的Service方法时,对于GET请求,代码逻辑如下: if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGe

解密浏览器缓存机制

浏览器缓存是节省用户流量,提升加载效率的常用方法:但同时它也会带来获取到历史脏数据等风险,今天我们就来详细介绍下浏览器缓存相关内容. 分类 浏览器的缓存主要包括两种缓存:强缓存.验证缓存. 1. 强缓存 强缓存是指浏览器不与服务器进行任何交互请求,直接将浏览器的缓存数据(包括缓存数据的 Response 头信息)返回给用户.这种缓存给用户的响应是最快的,但同时也是风险性较高的.因为该类缓存没有进行任何的校验即直接反馈给用户,是可能存在有历史的脏数据.当浏览器的请求出现同时以下两个现象时该次请求就

HTTP请求中浏览器缓存

本文导读:浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制.客户端缓存是否需要是可以在服务端代码上控制的.那就是响应头.响应头告诉缓存器不要保留缓存,缓存器就不会缓存相应内容:如果请求信息是需要认证或者安全加密的,相应内容也不会被缓存.那么,HTTP请求中浏览器是如何缓存数据呢? 一.HTTP请求中浏览器缓存流程图 1.浏览器第一次请求 2.浏览器再次请求时 二.HTTP请求中浏览器缓存流程的文字描述 1.当资源第一次被访问的时候,HTTP头部如下 (Request-Line) GET /

如何使用浏览器缓存来加快站点的访问速度 转载

当我们想尽办法提高服务器负载能力的时候,是否曾想过,其实浏览器也有缓存功能,我们也可以利用浏览器的缓存功能来减少服务器的负载,提高网站吞吐量?        当我们使用抓包工具查看http状态时,我们可以看到200,304,from cache之类的标识,也许你也早已注意到了他们,并明白他们的意思.但是,你是否想过在你的程序中加入代码主动告诉浏览器,更好地利用浏览器缓存. 备注:本章所有案例都是使用chrome 17.0.963.79 m浏览器,测试方式为每次打开浏览器新窗口的方式测试,而不是F

浏览器缓存相关http头

最近看雅虎黄金34条,学习下优化网站性能的方法.其中有一条:"为文件头指定Expires或Cache-Control",具体来说指对于静态内容:设置文件头过期时间Expires的值为"Never expire"永不过期:而对于动态内容:使用恰当的Cache-Control文件头来帮助浏览器进行有条件的请求. 这一条可以和雅虎34条的第一条联系起来,那就是尽量减少http的请求次数(雅虎34条很多都是努力去减少http请求为目的的),毕竟如果有太多的资源需要下载需要新

【转载】浏览器缓存详解:expires cache-control last-modified

最近在对CDN进行优化,对浏览器缓存深入研究了一下,记录一下,方便后来者 画了一个草图: 每个状态的详细说明如下: 1.Last-Modified 在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记(HttpReponse Header)此文件在服务期端最后被修改的时间,格式类似这样: Last-Modified:Tue, 24 Feb 2009 08:01:04 GMT 客户端第二次请求此URL时,根据HTTP协议