最代码官方的gravatar头像
最代码官方 2015-12-09 00:30:52

java servlet对html网页压缩后返回的过滤器代码实现

最近分享代码较少,正好在优化最代码关于返回的html代码的压缩上有些相关经验分享下。

之前研究的是通过freemarker的原生macro实现的:

<@compress single_line=true>
<html>
freemarker template技术
</html>
</@compress>

但因为最代码目前已经有几十个template文件,一个个加的话太费时费力了,另外如果不是freemarker技术的话就不能使用这个机制了,感觉java应该有这种技术来实现压缩的功能,于是想到了Filter

HtmlFilter.java

package com.zuidaima.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

/**
*@author www.zuidaima.com
**/
public class HtmlFilter implements Filter {

	public static final String codePatternStr = "(<|&lt;)p-re[\\w\\W]*?[\\w\\W]*?/p-re[\\w\\W]*?(>|&gt;)";
	public static final Pattern codePattern = Pattern.compile(codePatternStr,
			Pattern.CASE_INSENSITIVE);

	private FilterConfig filterConfig = null;

	class CodeFragment {

		private String left;
		private String fragment;
		private String right;

		public CodeFragment(String left, String fragment, String right) {
			this.left = left;
			this.fragment = fragment;
			this.right = right;
		}

		public String getLeft() {
			return left;
		}

		public void setLeft(String left) {
			this.left = left;
		}

		public String getFragment() {
			return fragment;
		}

		public void setFragment(String fragment) {
			this.fragment = fragment;
		}

		public String getRight() {
			return right;
		}

		public void setRight(String right) {
			this.right = right;
		}
	}

	private static class ByteArrayServletStream extends ServletOutputStream {
		ByteArrayOutputStream baos;

		ByteArrayServletStream(ByteArrayOutputStream baos) {
			this.baos = baos;
		}

		public void write(int param) throws IOException {
			baos.write(param);
		}
	}

	private static class ByteArrayPrintWriter {

		private ByteArrayOutputStream baos = new ByteArrayOutputStream();

		private PrintWriter pw = new PrintWriter(baos);

		private ServletOutputStream sos = new ByteArrayServletStream(baos);

		public PrintWriter getWriter() {
			return pw;
		}

		public ServletOutputStream getStream() {
			return sos;
		}

		byte[] toByteArray() {
			return baos.toByteArray();
		}
	}

	public class CharResponseWrapper extends HttpServletResponseWrapper {
		private ByteArrayPrintWriter output;
		private boolean usingWriter;

		public CharResponseWrapper(HttpServletResponse response) {
			super(response);
			usingWriter = false;
			output = new ByteArrayPrintWriter();
		}

		public byte[] getByteArray() {
			return output.toByteArray();
		}

		@Override
		public ServletOutputStream getOutputStream() throws IOException {
			// will error out, if in use
			if (usingWriter) {
				super.getOutputStream();
			}
			usingWriter = true;
			return output.getStream();
		}

		@Override
		public PrintWriter getWriter() throws IOException {
			// will error out, if in use
			if (usingWriter) {
				super.getWriter();
			}
			usingWriter = true;
			return output.getWriter();
		}

		public String toString() {
			return output.toString();
		}
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest _request = (HttpServletRequest) request;
		if (_request.getRequestURI().indexOf("update") != -1
				|| _request.getRequestURI().indexOf("create") != -1) {
			chain.doFilter(request, response);
			return;
		}
		CharResponseWrapper wrappedResponse = new CharResponseWrapper(
				(HttpServletResponse) response);
		chain.doFilter(request, wrappedResponse);
		byte[] bytes = wrappedResponse.getByteArray();

		String contentType = wrappedResponse.getContentType();
		if (contentType != null && contentType.matches(".*?(html|json).*?")) {
			String out = new String(bytes);
			Matcher matcher = codePattern.matcher(out);
			List<CodeFragment> codeFragments = new ArrayList<CodeFragment>();
			while (matcher.find()) {
				String fragment = matcher.group(0);
				String left = matcher.group(1);
				String right = matcher.group(2);
				CodeFragment codeFragment = new CodeFragment(left, fragment,
						right);
				codeFragments.add(codeFragment);
				// 占位符<pr-e>idx</pr-e>
				out = out.replace(fragment, left + "p-re" + right
						+ codeFragments.size() + left + "/p-re" + right);
			}
			out = out.replaceAll("[\r\n]", "").replaceAll(">\\s*?<", "><")
					.trim();
			// 还原占位符
			for (int i = 0; i < codeFragments.size(); i++) {
				CodeFragment codeFragment = codeFragments.get(i);
				String fragment = codeFragment.getFragment();
				String left = codeFragment.getLeft();
				String right = codeFragment.getRight();
				out = out.replace(left + "p-re" + right + (i + 1) + left
						+ "/p-re" + right, fragment);
			}
			response.getOutputStream().write(out.getBytes());
		} else {
			response.getOutputStream().write(bytes);
		}
	}

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

	@Override
	public void destroy() {
		filterConfig = null;
	}
}

刚上线的新鲜代码分享,相信对牛牛们会有所帮助吧。

注意:

1.创建和编辑类的操作不做压缩,因为访问量不大

2.<pre></pre>内部的代码不做处理

3.只对content type为html和json做的做处理

压缩前和压缩后的html代码大小对比如下图

java servlet对html网页压缩后返回的过滤器代码实现

压缩前源码

java servlet对html网页压缩后返回的过滤器代码实现

压缩后源码

java servlet对html网页压缩后返回的过滤器代码实现

另外对seo的影响可能不会很大,毕竟没有改变html结构。

平时多总结分享才能让自己的知识沉淀下来。


打赏

顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友