java版的YUI3 combine服务-Combo Handler

YUI3中,为了避免js文件过大,各个功能模块是拆分的。它有一个“种子”的概念:先下载一个小的核心的js文件到浏览器端,再通过这个小的js文件去加载其它所需的模块。

这种按需加载虽然解决了单个js过大的问题,但是随之带来另外一个问题:如果一个页面使用了YUI的a、b、c功能,那么浏览器就要向服务器请求a.js、b.js、c.js三个文件,这样增加了浏览器向服务器的沟通次数。

为了解决后面的问题,YUI3又有一个combine的概念,预先下载的那个核心的js,把页面上需要的a、b、c模块合并成一个请求发给服务器,类似这样:http://mydomain.com/conbine?a.js&b.js&c.js。

这要求服务器接收到http://mydomain.com/conbine请求后,将参数取出来,找到对应的a、b、c的js文件,合并成一个js文件,返回给客户端。Combo Handler是Yahoo!开发的一个Apache模块,专门来干这个事情的。

如果只用java的web容器,可以把这项工作委托给servlet:


<servlet>
    <servlet-name>yuicombo</servlet-name>
    <servlet-class>org.siqisource.mozo.servlets.YuiCombineServlet</servlet-class>
</servlet>
对应的YUI配置为:



YUI.GlobalConfig = {
    combine: true,
    comboBase: '<mz:webRoot/>/yuicombo?',
    root: 'uilibrary/yui/',
};


在servlet代码中,我使用了YUI的yuicompressor来压缩js和css文件,下面是maven配置。


<dependency>
    <groupId>com.yahoo.platform.yui</groupId>
    <artifactId>yuicompressor</artifactId>
    <version>2.4.7</version>
</dependency>


其中的Path类只是为了获得web应用物理路径,在是用的时候替换一下即可。

目前已知的缺陷:对于css的按需加载,浏览器会请求客户端两次,目前不清楚是不是YUI3(测试版本:3.7.2)的问题。

具体代码如下:


package org.siqisource.mozo.servlets;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileDeleteStrategy;
import org.apache.commons.io.FileUtils;
import org.siqisource.mozo.context.Path;

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

public class YuiCombineServlet extends HttpServlet {

	private static Map<String, String> cachedResources = new HashMap<String, String>();

	private String cacheContextPath = "uilibrary/yui/cache/";

	private String cacheDir = Path.getPhysicalPath() + cacheContextPath;

	int linebreakpos = -1;
	boolean munge = true;
	boolean verbose = false;
	boolean preserveAllSemiColons = false;
	boolean disableOptimizations = false;

	@Override
	public void init() throws ServletException {

		// 重置缓存文件夹
		File catchedDir = new File(cacheDir);

		if (catchedDir.exists()) {
			FileDeleteStrategy strategy = FileDeleteStrategy.FORCE;
			try {
				strategy.delete(catchedDir);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		catchedDir.mkdirs();

		super.init();
	}

	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		String queryString = request.getQueryString();
		String resourcePath = cachedResources.get(queryString);
		// 已缓存
		if (resourcePath == null) {

			String[] resources = queryString.split("&");
			String firstResource = resources[0];

			String fileName = UUID.randomUUID().toString();

			if (firstResource.endsWith(".js")) {

				fileName += ".js";
				Writer writer = new FileWriter(cacheDir + fileName);
				for (String resource : resources) {
					Reader reader = new FileReader(Path.getPhysicalPath()
							+ resource);
					JavaScriptCompressor compressor = new JavaScriptCompressor(
							reader, null);
					compressor.compress(writer, linebreakpos, munge, verbose,
							preserveAllSemiColons, disableOptimizations);
					reader.close();
				}
				writer.flush();
				writer.close();

			} else if (resources[0].endsWith(".css")) {
				fileName += ".css";
				Writer writer = new FileWriter(cacheDir + fileName);
				for (String resource : resources) {
					Reader reader = new FileReader(replacedUrlFile(resource));
					CssCompressor compressor = new CssCompressor(reader);
					compressor.compress(writer, linebreakpos);
					reader.close();
				}
				writer.flush();
				writer.close();
			}

			resourcePath = cacheContextPath + fileName;
			cachedResources.put(queryString, resourcePath);
		}
		request.getRequestDispatcher(resourcePath).forward(request, response);
		return;
	}
	
	public String replacedUrlFile(String fileName) throws IOException {

		String cssfilePath = Path.getPhysicalPath() + fileName;
		File cssFile = new File(cssfilePath);

		String tempCssFilePath = cacheDir + "tmp-css-" + cssFile.getName();
		File tempCssFile = new File(tempCssFilePath);
		if (tempCssFile.exists()) {
			return tempCssFilePath;
		}

		// 判断是否需要替换
		String css = FileUtils.readFileToString(cssFile);
		int maxIndex = css.length() - 1;
		int appendIndex = 0;
		Pattern p = Pattern.compile("url\\(\\s*([\"']?)");
		if (!p.matcher(css).find()) {
			return cssfilePath;
		}

		// 真的需要替换
		Matcher m = p.matcher(css);
		String url = fileName.substring(0, fileName.lastIndexOf('/'));
		url = Path.getContextPath() + "/" + url + "/";

		StringBuffer replacedUrlCss = new StringBuffer();

		while (m.find()) {
			int startIndex = m.start() + 4; // "url(".length()
			String terminator = m.group(1); // ', " or empty (not quoted)

			if (terminator.length() == 0) {
				terminator = ")";
			}

			boolean foundTerminator = false;

			int endIndex = m.end() - 1;
			while (foundTerminator == false && endIndex + 1 <= maxIndex) {
				endIndex = css.indexOf(terminator, endIndex + 1);

				if ((endIndex > 0) && (css.charAt(endIndex - 1) != '\\')) {
					foundTerminator = true;
					if (!")".equals(terminator)) {
						endIndex = css.indexOf(")", endIndex);
					}
				}
			}

			// Enough searching, start moving stuff over to the buffer
			replacedUrlCss.append(css.substring(appendIndex, m.start()));

			if (foundTerminator) {
				String token = css.substring(startIndex, endIndex);
				token = token.replaceAll("\\s+", "");
				String preserver = "url('" + url + token + "')";
				replacedUrlCss.append(preserver);

				appendIndex = endIndex + 1;
			} else {
				// No end terminator found, re-add the whole match. Should we
				// throw/warn here?
				replacedUrlCss.append(css.substring(m.start(), m.end()));
				appendIndex = m.end();
			}
		}
		FileUtils.writeStringToFile(tempCssFile, replacedUrlCss.toString());
		return tempCssFilePath;

	}
}


   


转载于:https://my.oschina.net/u/817827/blog/84189

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值