在我们浏览一个网站时,我们会发现,静态文件加载时传输速度占了很大一部分时间,尤其是在网速超慢的情况下,js、css文件压缩方案就出现了,据个人所知目前的压缩主要分为两类:
第一类,去掉js、css文件中的空格、注释,并且把长的变量函数名变短,比如我们常见的已min.js结尾的文件就是这样压缩后的文件。
第二类,就是我要说的GZIP压缩了,其原理是:在服务器端把js、css文件通过gzip压缩,把压缩包传到前端,浏览器解析。目前基本上主流的浏览器和应用服务器都支持GZIP技术,配置即可使用。
虽然配置即可使用,但是个人感觉也有不便之处。
其一,换了应用服务器,需要重新配置.
其二,每次需要用时才压缩,虽然可配置缓存,但这依然对服务器是个压力。
所以就在代码中实现GZIP压缩技术,实现跨平台、一次压缩多次使用,不以来如应用服务器
下来直接就说我的思路吧。首先配置拦截器,拦截所有js、css文件结尾的请求:
<filter> <filter-name>gzip</filter-name> <filter-class>xx.xx.GzipFilter</filter-class> </filter> <filter-mapping> <filter-name>gzip</filter-name> <url-pattern>*.js</url-pattern> </filter-mapping> <filter-mapping> <filter-name>gzip</filter-name> <url-pattern>*.css</url-pattern> </filter-mapping>
下来拦截器代码:
public class GzipFilter implements Filter {
private ServletContext ctx;
private Logger logger = Logger.getLogger(GzipFilter.class.getName());
private String contextPath;
private String realPath;
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String uri = req.getRequestURI();
String accept = req.getHeader("Accept-Encoding");
contextPath = ctx.getContextPath();
realPath = ctx.getRealPath("/");
uri = uri.substring(contextPath.length());
InputStream in = ctx.getResourceAsStream(uri + GZipUtils.EXT);
if (in == null) {
GzipThread gzipThread = new GzipThread(realPath + uri);
gzipThread.run();
}
if (accept != null && accept.contains("gzip") && (in = ctx.getResourceAsStream(uri + GZipUtils.EXT)) != null) {
logger.info("start getting gzip file " + uri);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] b = new byte[1024 * 8];
int read = 0;
while ((read = in.read(b)) >= 0) {
bout.write(b, 0, read);
}
in.close();
res.setHeader("Content-Encoding", "gzip");
if (uri.contains(".js")) {
res.setContentType("application/javascript;charset=UTF-8");
}
if (uri.contains(".css")) {
res.setContentType("text/css;charset=UTF-8");
}
res.setContentLength(bout.size());
ServletOutputStream out = res.getOutputStream();
out.write(bout.toByteArray());
out.flush();
logger.info("finish getting gzip file " + uri);
return;
} else {
chain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig config) throws ServletException {
ctx = config.getServletContext();
}
}
主要思路是判断是否存在压缩后的文件,如果不存在则开启一个线程去创建压缩文件,下来如果文件压缩好了则用压缩后文件,没压缩好直接不做操作。
下来线程代码:
public class GzipThread implements Runnable {
File file = null;
public GzipThread(String filePath) {
this.file = new File(filePath);
}
@Override
public void run() {
try {
GZipUtils.compress(file, false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
线程中调用一个产生GZIP文件的方法。
从网上找来的产生GZIP文件的代码:
public abstract class GZipUtils {
public static final int BUFFER = 1024;
public static final String EXT = ".gz";
/**
* 数据压缩
*
* @param data
* @return
* @throws Exception
*/
public static byte[] compress(byte[] data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 压缩
compress(bais, baos);
byte[] output = baos.toByteArray();
baos.flush();
baos.close();
bais.close();
return output;
}
/**
* 文件压缩
*
* @param file
* @throws Exception
*/
public static void compress(File file) throws Exception {
compress(file, true);
}
/**
* 文件压缩
*
* @param file
* @param delete
* 是否删除原始文件
* @throws Exception
*/
public static void compress(File file, boolean delete) throws Exception {
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file.getPath() + EXT);
compress(fis, fos);
fis.close();
fos.flush();
fos.close();
if (delete) {
file.delete();
}
}
/**
* 数据压缩
*
* @param is
* @param os
* @throws Exception
*/
public static void compress(InputStream is, OutputStream os) throws Exception {
GZIPOutputStream gos = new GZIPOutputStream(os);
int count;
byte data[] = new byte[BUFFER];
while ((count = is.read(data, 0, BUFFER)) != -1) {
gos.write(data, 0, count);
}
gos.finish();
gos.flush();
gos.close();
}
/**
* 文件压缩
*
* @param path
* @throws Exception
*/
public static void compress(String path) throws Exception {
compress(path, true);
}
/**
* 文件压缩
*
* @param path
* @param delete
* 是否删除原始文件
* @throws Exception
*/
public static void compress(String path, boolean delete) throws Exception {
File file = new File(path);
compress(file, delete);
}
/**
* 数据解压缩
*
* @param data
* @return
* @throws Exception
*/
public static byte[] decompress(byte[] data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 解压缩
decompress(bais, baos);
data = baos.toByteArray();
baos.flush();
baos.close();
bais.close();
return data;
}
/**
* 文件解压缩
*
* @param file
* @throws Exception
*/
public static void decompress(File file) throws Exception {
decompress(file, true);
}
/**
* 文件解压缩
*
* @param file
* @param delete
* 是否删除原始文件
* @throws Exception
*/
public static void decompress(File file, boolean delete) throws Exception {
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file.getPath().replace(EXT, ""));
decompress(fis, fos);
fis.close();
fos.flush();
fos.close();
if (delete) {
file.delete();
}
}
/**
* 数据解压缩
*
* @param is
* @param os
* @throws Exception
*/
public static void decompress(InputStream is, OutputStream os) throws Exception {
GZIPInputStream gis = new GZIPInputStream(is);
int count;
byte data[] = new byte[BUFFER];
while ((count = gis.read(data, 0, BUFFER)) != -1) {
os.write(data, 0, count);
}
gis.close();
}
/**
* 文件解压缩
*
* @param path
* @throws Exception
*/
public static void decompress(String path) throws Exception {
decompress(path, true);
}
/**
* 文件解压缩
*
* @param path
* @param delete
* 是否删除原始文件
* @throws Exception
*/
public static void decompress(String path, boolean delete) throws Exception {
File file = new File(path);
decompress(file, delete);
}
}
这样,我们在启动容器后,当有人第一次访问某个静态文件时,我们就在同目录下创建一个压缩文件,以后再次访问时就直接访问我们的静态文件了。当我们在浏览器查看资源的响应头信息时,如果有Content-Encoding:gzip字段表明配置成功。
当然,我们可以把两种压缩方法都使用起来,压缩效果更佳,先去空格、注释,修改变量,然后再用gzip压缩。