使用过滤器 配置jsp文件到缓存html 到redis

web.xml

<!-- jsp转成html过滤器 -->
	<filter>
		<filter-name>stateHtmlFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetBeanName</param-name>
			<param-value>stateHtmlFilter</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>stateHtmlFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
package cn.com.czw.front.filter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.List;
import java.util.Set;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import redis.clients.jedis.Jedis;
import cn.com.czw.front.utils.DateUtils;
import cn.com.czw.front.utils.RequestUtils;
import cn.com.czw.front.utils.StreamCharArrayWrapper;
import cn.com.czw.front.utils.Tools;

import com.google.common.collect.Lists;

@Component
public class StateHtmlFilter extends OncePerRequestFilter {

	private static Logger logger = LoggerFactory.getLogger(StateHtmlFilter.class);

	/** 是否已初始化 */
	private boolean isExternalUrlsInit = false;
	/** 排除不过滤的链接 */
	private String externalUrlsString = ".*/druid/.*,.*/manage/.*,.*/static/.*,.*/attachs/.*,.*/assets/.*,/user/.*;
	/** 认证要排除的url列表 */
	private List<String> externalUrls;

	/**
	 * 操作redis客户端
	 */
	private static Jedis jedis;

	@Autowired
	private JedisConnectionFactory jedisConnectionFactory;

	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
		// 请求的uri
		String uri = request.getRequestURI();
		// 如果是json请求直接绕过
		if (StringUtils.endsWith(uri,".json")) {
			chain.doFilter(request, response);
			return;
		}

		// String url = request.getRequestURL().toString();
		request.setCharacterEncoding("utf-8");
		response.setCharacterEncoding("utf-8");

		// 查看文件是否存在

		String method = "GET";
		method = request.getMethod();
		String queryString = request.getQueryString();
		if ("POST".equalsIgnoreCase(method)) {
			// 如果是POST请求直接绕过
			chain.doFilter(request, response);
			return;
		}

		if (StringUtils.isBlank(queryString)) {
			queryString = "";
		} else {
			queryString = StringUtils.remove(queryString, "clear=1");
			queryString = StringUtils.remove(queryString, "&clear=1");
		}

		String fileName = request.getRequestURL().toString().replaceAll(":", "").replaceAll("/", "#") + queryString;
		String html = get(fileName);

		logger.info("访问:" + uri + "---" + fileName);

		/**
		 * 判断文件是否存在 或者是否清除缓存
		 */
		HttpServletResponse httpRes = (HttpServletResponse) response;
		httpRes.setHeader("Content-Encoding", "text/html;charset=UTF-8");

		/**
		 * 设置缓存
		 * 
		 */
		response.setContentType("text/html");
		// servlet页面默认是不缓存的
		// 本页面允许在浏览器端或缓存服务器中缓存,时限为60秒。
		// 60秒之内重新进入该页面的话不会进入该servlet的
		java.util.Date date = new java.util.Date();
		response.setDateHeader("Last-Modified", date.getTime()); // Last-Modified:页面的最后生成时间
		response.setDateHeader("Expires", date.getTime() + 1000 * 60); // Expires:过时期限值
		response.setHeader("Cache-Control", "max-age=20,must-revalidate"); // Cache-Control来控制页面的缓存与否,public:浏览器和缓存服务器都可以缓存页面信息;
		response.setHeader("Pragma", "Pragma"); // Pragma:设置页面是否缓存,为Pragma则缓存,no-cache则不缓存

		String finalStr = "";
		if (StringUtils.isBlank(html) || RequestUtils.isClearCache(request) == true) {
			logger.info(RequestUtils.isClearCache(request) == true ? "清除页面缓存" : "第一次访问");
			// HttpServletResponseWrapper responseWrapper = new
			// HttpServletResponseWrapper(httpRes); // 创建自定义的应答对象
			StreamCharArrayWrapper responseWrapper = new StreamCharArrayWrapper(httpRes);
			chain.doFilter(request, responseWrapper); // 完成过滤连的业务
			// responseWrapper.getWriter().flush();
			// 取得存放输出数据的 char 型数组,并组织成String类型
			char[] responseChars = responseWrapper.toCharArray();
			finalStr = new String(responseChars);
			logger.info("当前请求的输出内容长度为:" + finalStr.length());
			int status = httpRes.getStatus();
			// 只有相应返回码status正确的response才进行网页静态化
			if ((status >= 200 && status < 300) || status == 304) {
				set(fileName, finalStr, 1 * 60 * 60 * 24);// 秒
				logger.info("当前请求的输出到redis,内容长度为:" + finalStr.length());
			}
			// 最后,将页面再次输出到客户端的页面上
			OutputStream finalOut = httpRes.getOutputStream();
			// 这句话的意思,使得放入流的数据是utf8格式
			finalOut.write(finalStr.getBytes("UTF-8"));
			logger.info("当前请求的输出内容输出到客户端页面,Finish...");
		}
		// 如果已经生成HTML,直接把请求转发到html文件。
		else {
			logger.info("页面已缓存到redis");
			// 最后,将页面再次输出到客户端的页面上
			httpRes.getWriter().print(html);
		}
	}

	protected void doFilterInternalFile(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
		// 请求的uri
		String uri = request.getRequestURI();
		String url = request.getRequestURL().toString();
		request.setCharacterEncoding("utf-8");
		response.setCharacterEncoding("utf-8");
		logger.info("访问:" + uri + "---" + url);

		// 查看文件是否存在
		String filePath = request.getSession().getServletContext().getRealPath("/") + "html/";
		filePath = filePath.replaceAll("file:/", "");
		filePath = filePath.replaceAll("%20", " ");
		String fileName = request.getRequestURL().toString().replaceAll(":", "").replaceAll("/", "#") + request.getQueryString() + DateUtils.today() + ".html";
		filePath = filePath + fileName;
		File file = new File(filePath);
		logger.info("路径filePath" + filePath);

		/**
		 * 删除昨天的文件
		 */
		String filaNameYesterDay = request.getRequestURL().toString().replaceAll(":", "").replaceAll("/", "#") + DateUtils.today() + ".html";
		File fileYesterDay = new File(filePath + filaNameYesterDay);
		if (fileYesterDay.isFile() == true && file.exists() == true) {
			Tools.deleteFile(fileName);
			logger.info("删除filePath" + fileYesterDay);
		}

		// 不允许浏览器端或缓存服务器缓存当前页面信息。
		/*
		 * response.setHeader( "Pragma", "no-cache" );
		 * response.setDateHeader("Expires", 0); response.addHeader(
		 * "Cache-Control", "no-cache" );//浏览器和缓存服务器都不应该缓存页面信息
		 * response.addHeader( "Cache-Control", "no-store"
		 * );//请求和响应的信息都不应该被存储在对方的磁盘系统中; response.addHeader( "Cache-Control",
		 * "must-revalidate" );
		 */// 于客户机的每次请求,代理服务器必须想服务器验证缓存是否过时;

		/*
		 * 判断文件是否存在 或者是否清除缓存
		 */
		if (file.isFile() == false || file.exists() == false || RequestUtils.isClearCache(request) == true) {
			logger.info(RequestUtils.isClearCache(request) == true ? "清除页面缓存" : "第一次访问");
			HttpServletResponse httpRes = (HttpServletResponse) response;
			httpRes.setHeader("Content-Encoding", "text/html;charset=UTF-8");
			// HttpServletResponseWrapper responseWrapper = new
			// HttpServletResponseWrapper(httpRes); // 创建自定义的应答对象
			StreamCharArrayWrapper responseWrapper = new StreamCharArrayWrapper(httpRes);
			chain.doFilter(request, responseWrapper); // 完成过滤连的业务
			// responseWrapper.getWriter().flush();
			// 取得存放输出数据的 char 型数组,并组织成String类型
			char[] responseChars = responseWrapper.toCharArray();
			final String finalStr = new String(responseChars);
			logger.info("当前请求的输出内容长度为:" + finalStr.length());
			int status = httpRes.getStatus();
			// 只有相应返回码status正确的response才进行网页静态化
			if ((status >= 200 && status < 300) || status == 304) {
				// 临时输出到一个url地址
				file.createNewFile();
				FileOutputStream tempOut = new FileOutputStream(filePath);
				// 构建FileOutputStream对象,文件不存在会自动新建
				OutputStreamWriter tempWriter = new OutputStreamWriter(tempOut, "UTF-8");
				tempWriter.append(finalStr);
				tempWriter.close();
				// 关闭输出流
				tempOut.close();
				logger.info("当前请求的输出服务器本地,内容长度为:" + finalStr.length());
			}
			// 最后,将页面再次输出到客户端的页面上
			OutputStream finalOut = httpRes.getOutputStream();
			// 这句话的意思,使得放入流的数据是utf8格式
			finalOut.write(finalStr.getBytes("UTF-8"));
			logger.info("当前请求的输出内容输出到客户端页面,Finish...");
		}
		// 如果已经生成HTML,直接把请求转发到html文件。
		else {
			logger.info("文件已存在");
			request.getRequestDispatcher("/html/" + fileName).forward(request, response);
		}
	}

	protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
		if (isExternalUrlsInit == false) {
			if (StringUtils.isNotBlank(externalUrlsString) == true) {
				externalUrls = Lists.newArrayList(externalUrlsString.split(","));
			}
			// 如果要排除过滤的路径变量是空的,则置为true,这样,下次就不会再执行这一段被始化代码
			isExternalUrlsInit = true;
		}
		String uri = request.getRequestURI();
		if (CollectionUtils.isEmpty(externalUrls) == false) {
			// 不为空,匹配的都不过滤,直接获取网站资源
			for (String patten : externalUrls) {
				if (uri.matches(patten) == true) {
					return true;
				}
			}
		}
		return false;
	}

	public void destroy() {

	}

	/**
	 * 通过key删除(字节)
	 * 
	 * @param key
	 */
	public void del(byte[] key) {
		this.getJedis().del(key);
	}

	/**
	 * 通过key删除
	 * 
	 * @param key
	 */
	public void del(String key) {
		this.getJedis().del(key);
	}

	/**
	 * 添加key value 并且设置存活时间(byte)
	 * 
	 * @param key
	 * @param value
	 * @param liveTime
	 */
	public void set(byte[] key, byte[] value, int liveTime) {
		this.set(key, value);
		this.getJedis().expire(key, liveTime);
	}

	/**
	 * 添加key value 并且设置存活时间
	 * 
	 * @param key
	 * @param value
	 * @param liveTime
	 */
	public void set(String key, String value, int liveTime) {
		this.set(key, value);
		this.getJedis().expire(key, liveTime);
	}

	/**
	 * 添加key value
	 * 
	 * @param key
	 * @param value
	 */
	public void set(String key, String value) {
		this.getJedis().set(key, value);
	}

	/**
	 * 添加key value (字节)(序列化)
	 * 
	 * @param key
	 * @param value
	 */
	public void set(byte[] key, byte[] value) {
		this.getJedis().set(key, value);
	}

	/**
	 * 获取redis value (String)
	 * 
	 * @param key
	 * @return
	 */
	public String get(String key) {
		String value = this.getJedis().get(key);
		return value;
	}

	/**
	 * 获取redis value (byte [] )(反序列化)
	 * 
	 * @param key
	 * @return
	 */
	public byte[] get(byte[] key) {
		return this.getJedis().get(key);
	}

	/**
	 * 通过正则匹配keys
	 * 
	 * @param pattern
	 * @return
	 */
	public Set<String> keys(String pattern) {
		return this.getJedis().keys(pattern);
	}

	/**
	 * 检查key是否已经存在
	 * 
	 * @param key
	 * @return
	 */
	public boolean exists(String key) {
		return this.getJedis().exists(key);
	}

	/**
	 * 清空redis 所有数据
	 * 
	 * @return
	 */
	public String flushDB() {
		return this.getJedis().flushDB();
	}

	/**
	 * 查看redis里有多少数据
	 */
	public long dbSize() {
		return this.getJedis().dbSize();
	}

	/**
	 * 检查是否连接成功
	 * 
	 * @return
	 */
	public String ping() {
		return this.getJedis().ping();
	}

	/**
	 * 获取一个jedis 客户端
	 * 
	 * @return
	 */
	private Jedis getJedis() {
		if (jedis == null) {
			return jedisConnectionFactory.getShardInfo().createResource();
		}
		return jedis;
	}
}

util

package cn.com.czw.front.utils;
import java.io.CharArrayWriter;  
import java.io.PrintWriter;  
import javax.servlet.http.HttpServletResponse;  
import javax.servlet.http.HttpServletResponseWrapper;  
  
/** 
 * A response wrapper that takes everything the client would normally output and 
 * saves it in one big character array. 
 */  
public class StreamCharArrayWrapper extends HttpServletResponseWrapper {  
    private CharArrayWriter charWriter;  
  
    /** 
     * Initializes wrapper. 
     * <P> 
     * First, this constructor calls the parent constructor. That call is 
     * crucial so that the response is stored and thus setHeader, *setStatus, 
     * addCookie, and so forth work normally. 
     * <P> 
     * Second, this constructor creates a CharArrayWriter that will be used to 
     * accumulate the response. 
     */  
    public StreamCharArrayWrapper(HttpServletResponse response) {  
        super(response);  
        charWriter = new CharArrayWriter();  
    }  
  
    /** 
     * When servlets or JSP pages ask for the Writer, don't give them the real 
     * one. Instead, give them a version that writes into the character array. 
     * The filter needs to send the contents of the array to the client (perhaps 
     * after modifying it). 
     */  
    @Override  
    public PrintWriter getWriter() {  
        return new PrintWriter(charWriter);  
    }  
  
    /** 
     * Get a String representation of the entire buffer. 
     * <P> 
     * Be sure <B>not</B> to call this method multiple times on the same 
     * wrapper. The API for CharArrayWriter does not guarantee that it 
     * "remembers" the previous value, so the call is likely to make a new 
     * String every time. 
     */  
    @Override  
    public String toString() {  
        return charWriter.toString();  
    }  
  
    /** Get the underlying character array. */  
    public char[] toCharArray() {  
        return charWriter.toCharArray();  
    }  
}  

猜你喜欢

转载自my.oschina.net/u/1052192/blog/1023395
今日推荐