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();
}
}