java版的YUI3 combine服务-Combo Handler

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

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

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

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

具体代码以下: dom


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;

	}
}
相关文章
相关标签/搜索