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;

	}
}
相關文章
相關標籤/搜索