在实际开发中,我们不可避免需要优化输出的内容,使得用户体验优化的效果。
其中,在Web开发当中就有tomcat的gzip来压缩输出流,使得网络带宽压力下降,从而提升浏览器的文件下载速度。这个办法的好处是配置简单,资源较多的情况下效果明显。
但是也有缺点:
1、增加CPU的负担。故此方法不适合CPU资源紧张的服务器。
2、压缩得还不算彻底。
3、需要相应支持的浏览器。
哪怕有上面这些问题,总体上来讲,这个技术还是利大于弊。毕竟CPU便宜过带宽。
为了压缩得更加彻底,我们可以尝试在gzip的基础上,实现一个进一步的代码压缩,把所有jsp、HTML、css、js这些资源文件自动输出成mini版。这样就可以到达进一步压缩的目的。但是会加深CPU、RAM的消耗以及,这个方法是通过自行写代码实现,所以有一定的难度以及可能不够灵活。
具体实现方式是:
1、继承javax.servlet.http.HttpServletResponseWrapper来编写一个Java类。
2、创建一个专门处理资源文件输出的Filter。
3、实现一段处理字符串的逻辑。
4、Filter过滤全部的请求,并且里面要分开各种资源请求的类型来进行不同处理。
5、字符串输出。
继承HttpServletResponseWrapper的Java代码:
import java.io.CharArrayWriter;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class ResponseWrapper extends HttpServletResponseWrapper {
private CharArrayWriter out;
public ResponseWrapper(HttpServletResponse response) {
super(response);
out = new CharArrayWriter();
}
public PrintWriter getWriter() {
return new PrintWriter(out);
}
public String toString() {
return getCharArrayWriter().toString();
}
public CharArrayWriter getCharArrayWriter() {
return out;
}
}
负责过滤请求并返回处理后的信息的Filter以及内部写的一些处理方法:
import java.io.IOException;
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 org.apache.commons.lang.StringUtils;
import com.winsun.warpper.ResponseWrapper;
/**
* 专门用于对输出的内容进行格式化
* 可以起到一定的保密作用,以及优化服务器的流量带宽,但是对服务器CPU、RAM有一定的消耗
* @author Lph
* @date 2015年11月27日
*/
public class HTMLFilter implements Filter {
//用于控制本FIlter的功能是否启动
private static boolean FILTER_SWITCH = true;
public void destroy() {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
if(FILTER_SWITCH) {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
String servletPath = request.getServletPath();
String url = request.getContextPath() + servletPath;
if(url.endsWith(".jsp") || url.endsWith(".action")) {
ServletOutputStream outStream = servletResponse.getOutputStream();
ResponseWrapper wrapper = new ResponseWrapper(response);
chain.doFilter(request, wrapper);
String code = wrapper.toString();
String[] rows = code.split("\n");
StringBuffer codeBuffer = new StringBuffer();
String row = null;
for(int i = 0 ; i < rows.length; i++) {
row = rows[i];
row = clearTabcharacter(row);
row = clearRowTheNodes(row);
if(StringUtils.isBlank(row) || row.equals("\n")) {
//清除空行
continue;
}
codeBuffer.append(row);
}
String result = codeBuffer.toString();
/*if(url.endsWith("css") || url.endsWith("js") || url.endsWith("map")) {
System.out.println("Output Content:");
System.out.println(result);
}*/
//result = clearRowsTheNodes(result);
outStream.write(result.getBytes("UTF-8"));
outStream.flush();
outStream.close();
} else {
chain.doFilter(request, response);
}
} else {
chain.doFilter(servletRequest, servletResponse);
}
}
public void init(FilterConfig config) throws ServletException {
String filterSwitch = config.getInitParameter("FilterSwitch");
FILTER_SWITCH = StringUtils.isBlank(filterSwitch) || filterSwitch.equals("on");
}
/**
* 清除字符串对象的其实制表符和结束的制表符
* @param row
* @return
*/
public String clearTabcharacter(String row) {
char[] chars = row.toCharArray();
int startIndex = -1, endIndex = -1;
int i = 0;
for(i = 0; i < chars.length; i++) {
if(chars[i] == "\t".charAt(0) || chars[i] == " ".charAt(0)) {
startIndex = i;
} else {
break;
}
}
for(i = chars.length - 1; i >= 0; i--) {
if(chars[i] == "\t".charAt(0) || chars[i] == " ".charAt(0)) {
endIndex = i;
} else {
break;
}
}
if(startIndex < 0) {
return row;
} else {
if(endIndex < 1) {
endIndex = row.length();
}
return row.substring(startIndex + 1, endIndex);
}
}
/**
* 清除行注释,仅仅是判断"//"
* 满足则返回空字符串。否则原样返回
* @param row 一行的内容
* @return
*/
public String clearRowTheNodes(String row) {
if(row.startsWith("//")) {
row = "";
}
return row;
}
public String clearRowsTheNodes(String content) {
StringBuffer code = new StringBuffer();
char[] chars = content.toCharArray();
boolean captureStart = false, captrueEnd = false;
for(int i = 0; i < chars.length; i++) {
if(i + 1 < chars.length) {
if(chars[i] == "/".charAt(0) && chars[i + 1] == "*".charAt(0)) {
captureStart = true;
}
if(chars[i] == "*".charAt(0) && chars[i + 1] == "/".charAt(0)) {
captrueEnd = true;
}
if(captureStart == false && captrueEnd == false) {
code.append(chars[i]);
captureStart = false;
captrueEnd = false;
}
}
}
return code.toString();
}
}
- 这里仅仅是举例,没有彻底完成所以并不能完成的mini输出。
- 目前贴出来的代码仅仅支持jsp。其他资源文件还没处理。
- 支持JSP内“//”这种行注释的去除。
- 具体可以达到什么样的效果要看代码的实现。
- 自动输出迷你版在自动去除注释的情况下可以起到一定的保密作用。
- 方便更新版本,不需要每次修改文件都生成一次迷你版。
- 此方法是在jsp编译完毕之后才会进行处理,故jstl\el等情况不需要考虑
继承HttpServletResponseWrapper的Java代码: