java 压缩css_分享一个JS/CSS 在线合并压缩的方案-Compressor-Java 实现

大家好,这几天正好做站点优化,也看了写工具,在线的非在线的。

如:grunt,php的minify,yui等

今天给大家介绍下我引用了YUI做了在线的效果,主要生成文件以及合并CSS的效果。下面介绍下过程;

1、由于我使用的是Freemarker 去做的View 所有我做了个标签; package me.duzhi.blog.plugins.compress;

import com.jfinal.kit.StrKit;

import freemarker.core.Environment;

import freemarker.ext.beans.BeansWrapper;

import freemarker.ext.beans.BeansWrapperBuilder;

import freemarker.template.*;

import java.io.IOException;

import java.util.List;

import java.util.Map;

/**

* @author [email protected]

* @date 一月 07, 2017

*/

public class CompressDirective implements TemplateDirectiveModel {

@SuppressWarnings("rawtypes")

public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)

throws TemplateException, IOException {

Object var = params.get("var");

if (var == null) {

throw new TemplateModelException("assets tag attribute var can not be null!");

}

String varName = var.toString();

if (StrKit.isBlank(varName)) {

throw new TemplateModelException("assets tag attribute var can not be null!");

}

Object file = params.get("file");

if (file == null) {

throw new TemplateModelException("assets tag attribute file can not be null!");

}

String fileName = file.toString();

if (StrKit.isBlank(fileName)) {

throw new TemplateModelException("assets tag attribute file can not be null!");

}

Object ido = params.get("id");

if (ido == null) {

throw new TemplateModelException("assets tag attribute id can not be null!");

}

String id = ido.toString();

if (StrKit.isBlank(id)) {

throw new TemplateModelException("assets tag attribute id can not be null!");

}

List path = CompressKit.getPath(id, fileName);

BeansWrapper beansWrapper = new BeansWrapperBuilder(Configuration.getVersion()).build();

for (String s : path) {

env.setVariable(varName, beansWrapper.wrap(s));

body.render(env.getOut());

}

}

}

上面是标签的代码;

在下面是核心代码: package me.duzhi.blog.plugins.compress;

import com.jfinal.kit.HashKit;

import com.jfinal.kit.PathKit;

import com.jfinal.kit.StrKit;

import com.jfinal.log.Log;

import com.yahoo.platform.yui.compressor.CssCompressor;

import com.yahoo.platform.yui.compressor.JavaScriptCompressor;

import org.apache.commons.io.FileUtils;

import org.apache.commons.io.FilenameUtils;

import org.apache.commons.io.IOUtils;

import org.mozilla.javascript.ErrorReporter;

import org.mozilla.javascript.EvaluatorException;

import java.io.*;

import java.net.URL;

import java.nio.charset.Charset;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.ConcurrentMap;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

/**

* @author [email protected]

* @date 一月 07, 2017

*/

public class CompressKit {

private static final Log log = Log.getLog(CompressKit.class);

private static final Charset UTF_8 = Charset.forName("UTF-8");

private static final String JS_EXT = ".js", CSS_EXT = ".css";

private static final String PROTOCOL = "^https?://.+$";

// 考虑到线上环境基本不会频繁更改css,js文件为了性能故缓存

public static ConcurrentMap COMBO_MAP = new ConcurrentHashMap();

/**

* 压缩工具

*

*

* @param name

* @param fileName 待压缩的文件列表文件 /assets/assets.jjs

* @return String 返回压缩完成之后的路径

* @throws IOException 文件不存在时异常

*/

public static List getPath(String name, String fileName) throws IOException {

List pathList = new ArrayList();

String rootPath = PathKit.getWebRootPath();

// 路径判读

if (!fileName.startsWith("/")) {

fileName = "/" + fileName;

}

String path = COMBO_MAP.get(fileName);

if (StrKit.isBlank(path)) {

return combo(rootPath, fileName);

}

File assetsFile = new File(rootPath + path);

// 文件存在则直接返回路径

if (assetsFile.exists()) {

pathList.add(path);

return pathList;

}

return combo(rootPath, fileName);

}

/**

* 压缩css,js帮助

*

* @param rootPath 项目路径

* @param fileList 合并压缩的文件列表

* @param isCss 是否是css

* @param out 输出流

* @throws IOException Io异常

*/

private static void compressorHelper(String rootPath, List fileList, boolean isCss, Writer out) throws IOException {

Reader in = null;

InputStream input = null;

try {

if (isCss) {

for (String path : fileList) {

boolean isRomte = isRomte(path);

input = isRomte ? new URL(path).openStream() : new FileInputStream(rootPath + path);

// css 文件内容,并处理路径问题

String context = repairCss(IOUtils.toString(input, UTF_8), path);

if (path.indexOf(".min.") > 0 || isRomte) {// 对.min.css的css放弃压缩

out.append(context);

} else {

CssCompressor css = new CssCompressor(new StringReader(context));

css.compress(out, -1);

}

input.close();

input = null;

}

} else {

// nomunge: 混淆,verbose:显示信息消息和警告,preserveAllSemiColons:保留所有的分号 ,disableOptimizations 禁止优化

boolean munge = true, verbose = false, preserveAllSemiColons = false, disableOptimizations = false;

for (String path : fileList) {

boolean isRomte = isRomte(path);

input = isRomte ? new URL(path).openStream() : new FileInputStream(rootPath + path);

in = new InputStreamReader(input, UTF_8);

if (path.indexOf(".min.") > 0 || isRomte) { // 对.min.js,和远程js放弃压缩

out.append(IOUtils.toString(in));

} else {

JavaScriptCompressor compressor = new JavaScriptCompressor(in, new ErrorReporter() {

public void warning(String message, String sourceName,

int line, String lineSource, int lineOffset) {

if (line < 0) {

log.error("\n[WARNING] " + message);

} else {

log.error("\n[WARNING] " + line + ':' + lineOffset + ':' + message);

}

}

public void error(String message, String sourceName,

int line, String lineSource, int lineOffset) {

if (line < 0) {

log.error("\n[ERROR] " + message);

} else {

log.error("\n[ERROR] " + line + ':' + lineOffset + ':' + message);

}

}

public EvaluatorException runtimeError(String message, String sourceName,

int line, String lineSource, int lineOffset) {

error(message, sourceName, line, lineSource, lineOffset);

return new EvaluatorException(message);

}

});

compressor.compress(out, -1, munge, verbose, preserveAllSemiColons, disableOptimizations);

}

input.close();

input = null;

in.close();

in = null;

}

}

out.flush();

} catch (IOException e) {

throw e;

} finally {

IOUtils.closeQuietly(in);

IOUtils.closeQuietly(input);

}

}

/**

* 将css文件里的图片相对路径修改为绝对路径

*

* @param content 内容

* @param path 路径

* @return String css

*/

private static String repairCss(String content, String path) {

Pattern p = Pattern.compile("url\\([\\s]*['\"]?((?!['\"]?https?://|['\"]?data:|['\"]?/).*?)['\"]?[\\s]*\\)"); // 感谢Code Life(程式人生)的正则

Matcher m = p.matcher(content);

StringBuffer sb = new StringBuffer();

while (m.find()) {

String url = m.group(1).trim();

StringBuffer cssPath = new StringBuffer("url(").append(FilenameUtils.getFullPath(path)).append(url).append(")");

m.appendReplacement(sb, cssPath.toString());

}

m.appendTail(sb);

content = sb.toString();

return content;

}

/**

* 压缩工具

*

* @param rootPath 项目路径

* @param fileName 待压缩的文件列表文件 /assets/assets.jjs

* @return String 返回压缩完成之后的路径

* @throws IOException 文件不存在时异常

*/

private static List combo(String rootPath, String fileName) throws IOException {

File assetsConfig = new File(rootPath + fileName);

// 待压缩的文件列表不存在时抛出异常

if (!assetsConfig.exists()) {

throw new IOException(fileName + " not found...");

}

// 读取文件中的js或者css路径

List list = FileUtils.readLines(assetsConfig, UTF_8);

// 文件内容md5

StringBuilder fileMd5s = new StringBuilder();

StringBuilder config = new StringBuilder();

Iterator it = list.iterator();

String lines;

while (it.hasNext()) {

lines = it.next();

config.append(lines);

if (StrKit.isBlank(lines)) {

it.remove();

continue;

}

// 去除首尾空格

lines = lines.trim();

// #开头的行注释

if (lines.startsWith("#")) {

it.remove();

continue;

}

// 远程服务器上的资源文件,不参与MD5

if (isRomte(lines)) {

continue;

}

// 对错误地址修复

if (!lines.startsWith("/")) {

lines = "/" + lines;

}

String filePath = rootPath + lines;

File file = new File(filePath);

if (!file.exists()) {

throw new IOException(file.getName() + " not found...");

}

String content = FileUtils.readFileToString(file, UTF_8);

fileMd5s.append(HashKit.md5(content));

}

fileMd5s.append(HashKit.md5(config.toString()));

/* if (Jpress.isDevMode()) {

return list;

}*/

// 文件更改时间集合hex,MD5取中间8位

String hex = HashKit.md5(fileMd5s.toString()).substring(8, 16);

boolean isCss = true;

if (fileName.endsWith(".jjs")) {

isCss = false;

}

// /assets/assets.jjs

int index = fileName.lastIndexOf("/");

fileName = fileName.substring(0,index+1)+"min/"+fileName.substring(index+1);

String comboName = fileName.substring(0, fileName.indexOf('.'));

String newFileName = comboName + '-' + hex + (isCss ? CSS_EXT : JS_EXT);

String newPath = rootPath + newFileName;

File file = new File(newPath);

// 判断文件是否已存在,已存在直接返回

if (file.exists()) {

list.clear();

list.add(newFileName);

return list;

}

// 将合并的结果写入文件,异常时将文件删除

OutputStream output = null;

Writer out = null;

try {

output = new FileOutputStream(newPath);

out = new OutputStreamWriter(output, UTF_8);

compressorHelper(rootPath, list, isCss, out);

// 装载文件路径

COMBO_MAP.put(fileName, newFileName);

} catch (Exception e) {

FileUtils.deleteQuietly(file);

log.error(e.getMessage(), e);

throw new RuntimeException(fileName + " 压缩异常,请检查是否有依赖问题!");

} finally {

IOUtils.closeQuietly(output);

IOUtils.closeQuietly(out);

}

list.clear();

list.add(newFileName);

return list;

}

/**

* 判断文件是否为远程资源文件,远程资源文件不进行压缩

*

* @param path

* @return

*/

private static boolean isRomte(String path) {

if (StrKit.isBlank(path)) {

return false;

}

return path.trim().matches(PROTOCOL);

}

}

大家自行参考哦,又需要的可以根据需要更新。

更新个:

com.yahoo.platform.yui

yuicompressor

2.4.8

commons-io

commons-io

2.5

最终效果:

1460000008051871?w=240&h=160

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值