使用過濾器 配置jsp文件到緩存html 到redis

web.xmlhtml

<!-- 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;
	}
}

utiljava

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();  
    }  
}
相關文章
相關標籤/搜索