使用过滤器Filter实现网页静态化

17 篇文章 0 订阅
6 篇文章 0 订阅

设计目标:

          实现网页的静态化。

设计思路:

         实现方法有三种:

1. 通过动态模板引擎FreeMarker、Velocity渲染后,直接输出到一个静态文件中。

2. 通过Nginx的相关插件(lighttpd、squid)做静态化处理,具体可以参考:http://www.oschina.net/question/54100_9105

3. 通过Servlet的过滤器,在输出阶段,获取输出流,截取输出字段,继续输出流,并将输出字段写到一个html文件中。

代码实现:

1. Web.xml的配置

<!-- 静态页面 -->
	<filter>
		<filter-name>cacheHtmlFilter</filter-name>
		<filter-class>com.wll.web.filter.CacheHtmlFilter</filter-class>

		<init-param>
			<param-name>isGenHtmlFile</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>cacheHtmlFilter</filter-name>
		<url-pattern>/path1/*</url-pattern>
	</filter-mapping>
	<filter-mapping>
		<filter-name>cacheHtmlFilter</filter-name>
		<url-pattern>/path2/*</url-pattern>
	</filter-mapping>
	<filter-mapping>
		<filter-name>cacheHtmlFilter</filter-name>
		<url-pattern>/path3/*</url-pattern>
	</filter-mapping>


2.Filter的实现

package net.wll.web.filter;

import net.xuele.common.security.SessionUtil;
import net.xuele.common.security.UserSession;
import net.xuele.common.utils.format.JsonUtils;
import net.xuele.teacheval.web.utils.EvalRedisUtil;
import net.xuele.teacheval.web.wrapper.StreamCharArrayWrapper;
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.core.StringRedisTemplate;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * 静态化过滤器 <br>
 * 截获输出流,获取输出字节,做相关的静态化处理,并输出到页面
 *
 * @author wull
 */
public class CacheHtmlFilter implements Filter {
	private static final Logger logger = LoggerFactory.getLogger(CacheHtmlFilter.class);

	/** 是否输出到静态页面 */
	private static final boolean isGenHtmlFile = false;
	/** 是否将输出字节保存到Cache中 */
	private static final boolean isSaveStaticHtmlToCache = true;

	private StringRedisTemplate cacheTeachevalRedisTemplate;

	@Override
	public void init(FilterConfig config) throws ServletException {
		logger.debug("-----------------》初始化11111111111111111111111111111111111");
		if(this.cacheTeachevalRedisTemplate == null) {
			ServletContext servletContext = config.getServletContext();
			WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
			cacheTeachevalRedisTemplate = (StringRedisTemplate) ctx.getBean("cacheTeachevalRedisTemplate");
		}
	}

	/**
	 * 执行网页静态化处理
	 * @param request
	 * @param response
	 * @param chain
	 */
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
						 FilterChain chain) throws ServletException, IOException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse resp = (HttpServletResponse) response;
		this.execFilter(SessionUtil.getUserSession(), req, resp,  chain);
	}

	public void execFilter(UserSession userSession, HttpServletRequest req, HttpServletResponse resp,
						   FilterChain chain) throws ServletException, IOException {

		// 1. 获取当前的url信息
		String url = req.getRequestURI().substring(req.getContextPath().length());
		String queryString = req.getQueryString();
		String key = url;
		try {
			// 2. 通过request获取所有的parameter,用来组织key值
			Map<String, String[]> reqParams = req.getParameterMap();
			key = EvalRedisUtil.genCachePageKey(url, userSession, reqParams);

			// 2.1 如果通过模糊查询的,则不进行过滤
			String keyword = req.getParameter("keyword");
			if(StringUtils.isNotBlank(keyword)){
				chain.doFilter(req,resp);
				return ;
			}

			String value = EvalRedisUtil.get(this.cacheTeachevalRedisTemplate, key);
			// 3.看看缓存中是否有该缓存页面,有的话直接返回
			if (StringUtils.isBlank(value)) {
				// 3.1 如果缓存中找不到对应的value值,那么直接返回动态页面
				logger.debug("缓存中key={}找不到对应的value值,那么直接返回动态页面", key);
			} else {
				// 3.2 如果缓存中找到对应的value值,那么直接输出流
				logger.debug("缓存中key={}找到对应的value.size={},输出到客户端页面上", new String[]{key, String.valueOf(value.length())});

				OutputStream finalOut = resp.getOutputStream();
				//这句话的意思,使得放入流的数据是utf8格式
				finalOut.write(value.getBytes("UTF-8"));
				logger.debug("当前请求的输出内容输出到客户端页面,Finish...");
				return;
			}

		}catch (Exception e){
			// 如果中间出现异常,直接返回动态页面
			logger.error("网页静态化--处理网页静态化时出现异常,直接返回动态页面,异常信息为:", e);
			chain.doFilter(req,resp);
			return ;
		}

		// 4. 调用资源,使用 StreamCharArrayWrapper 包装输出,并获取页面字节码
		resp.setHeader("Content-Encoding", "text/html;charset=UTF-8");
		resp.setCharacterEncoding("UTF-8");
		StreamCharArrayWrapper responseWrapper = new StreamCharArrayWrapper(resp);
		chain.doFilter(req, responseWrapper);

		// 5. 取得存放输出数据的 char 型数组,并组织成String类型
		char[] responseChars = responseWrapper.toCharArray();
		final String finalStr = new String(responseChars);
		logger.debug("当前请求的输出内容长度为:" + finalStr.length());
		int status = resp.getStatus();

		// 6. 只有相应返回码status正确的response才进行网页静态化
		if((status >= 200 && status < 300) || status == 304 ){
			try{
				// correct do nothing
				logger.info("当前请求的url:{},queryString:{},status={},OK...", new String[]{url, queryString,String.valueOf(status)});
				// 6.1.是否输出到服务器本地静态页面
				if(isGenHtmlFile == true){
					ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
					// 临时输出到一个url地址
					FileOutputStream tempOut = new FileOutputStream("C://"+new Random().nextInt(1000)+".html");
					// 构建FileOutputStream对象,文件不存在会自动新建
					OutputStreamWriter tempWriter = new OutputStreamWriter(tempOut, "UTF-8");
					tempWriter.append(finalStr);
					tempWriter.close();
					// 关闭输出流
					tempOut.close();
					logger.debug("当前请求的输出服务器本地,内容长度为:" + finalStr.length());
				}

				// 6.2 是否将输出字节保存到Cache中
				if(isSaveStaticHtmlToCache == true){
					// 6.2 保存到缓存中,加入到缓存
					EvalRedisUtil.set(this.cacheTeachevalRedisTemplate,key,finalStr);
					logger.debug("当前请求的输出内容保存到缓存当中,key={},value.size={}" ,new String[]{key,String.valueOf(finalStr.length())});
				}

			}catch (Exception e){
				// 如果中间出现异常,直接返回动态页面
				logger.error("网页静态化--处理网页静态化时出现异常,直接返回动态页面,异常信息为:",e);
				chain.doFilter(req,resp);
				return ;
			}
		}else{
			// 有错误的response,直接返回动态页面
			logger.info("当前请求的url:{},queryString:{},status={},ERROR...", new String[]{url, queryString, String.valueOf(status)});
		}
		// 7. 最后,将页面再次输出到客户端的页面上
		OutputStream finalOut = resp.getOutputStream();
		//这句话的意思,使得放入流的数据是utf8格式
		finalOut.write(finalStr.getBytes("UTF-8"));
		logger.debug("当前请求的输出内容输出到客户端页面,Finish...");
	}

	@Override
	public void destroy() {
	}

	/**
	 * 因为Filter在单元测试期间,无法
	 * @param cacheTeachevalRedisTemplate
	 */
	public void setCacheTeachevalRedisTemplate(StringRedisTemplate cacheTeachevalRedisTemplate) {
		this.cacheTeachevalRedisTemplate = cacheTeachevalRedisTemplate;
		logger.debug("-----------------》初始化222222222222222222222222222222222222");
	}
}



3. StreamCharArrayWrapper将输出流变成char数组

package com.wll.web.wrapper;

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值