package com.qxueyou.scc.base.util; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; /** * 前三个属性根据实际存储位置设置 * @author ody.yuan * */ public class MyJsCssVersionUtils { /** web工程的发布路径 */ private static String webContent = "qxueyou"; /** web工程中web文件夹的绝对路径 D:\project\workspace_web\versioncontroller0\web\js\qxueyou.js */ /** web工程中web文件夹的绝对路径 D:\project\workspaceStkkk\qxueyou_web\web\js\qxueyou.js */ private static String webFileAbsolutePath = "C:" + File.separator + "workspace" + File.separator + "qxueyou_web"; /** 扫描js、css、html文件的文件夹绝对路径,固定设置为web目录 */ private static String fileScanDirec = "C:" + File.separator + "workspace" + File.separator + "qxueyou_web"; /** 匹配html中引入js文件 */ private static Pattern scriptImportPattrrn = Pattern.compile(""); /** 匹配html中引入css文件 */ private static Pattern cssImportPattrrn = Pattern.compile(""); private static char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; /** * 得到处理过的文件路径为key,文件md5值为value的map * @param filePath 文件路径 * @param fileExtension 文件类型 * @param map map */ private static void getFileMD5Map(String filePath,String fileExtension,Map map,String version){ readFile(filePath,fileExtension,map,version); } /** * 得到路径下的所需文件和文件的md5值 * @param filePath * @param fileExtension * @param map */ private static void readFile(String filePath,String fileExtension,Map map,String version){ File file = new File(filePath); if(file.isFile()){ String fiePath = file.getAbsolutePath(); //符合指定类型 if(fiePath.endsWith(fileExtension)){ generateMap(file,fileExtension,map,version); } }else if(file.isDirectory()){ String[] fileList = file.list(); for(String name : fileList){ readFile(filePath + File.separator + name,fileExtension,map,version); } } } /** * 得到一个文件的map * @param file * @param map */ private static void generateMap(File file, String fileExtension, Map map,String version){ //TODO 缩短md5值 String strKey = file.getAbsolutePath(); strKey = strKey.replace(webFileAbsolutePath, File.separator + webContent); strKey = strKey.replace("\\", "/"); String strValue = getMD5(file); if(StringUtils.isNotBlank(strValue)){ strValue = version; } map.put(strKey, strValue); } /** * 对文件全文生成MD5摘要 * * @param file * 要加密的文件 * @return MD5摘要码 */ public static String getMD5(File file) { FileInputStream fis = null; try { MessageDigest md = MessageDigest.getInstance("MD5"); fis = new FileInputStream(file); byte[] buffer = new byte[2048]; int length = -1; //暂时不用 //long s = System.currentTimeMillis(); while ((length = fis.read(buffer)) != -1) { md.update(buffer, 0, length); } byte[] b = md.digest(); return byteToHexString(b); // 16位加密 // return buf.toString().substring(8, 24); } catch (Exception ex) { ex.printStackTrace(); return null; } finally { try { fis.close(); } catch (IOException ex) { ex.printStackTrace(); } } } /** * 把byte[]数组转换成十六进制字符串表示形式 * @param tmp 要转换的byte[] * @return 十六进制字符串表示形式 */ private static String byteToHexString(byte[] tmp) { String s; // 用字节表示就是 16 个字节 char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符, // 所以表示成 16 进制需要 32 个字符 int k = 0; // 表示转换结果中对应的字符位置 for (int i = 0; i < 16; i++) { // 从第一个字节开始,对 MD5 的每一个字节 // 转换成 16 进制字符的转换 byte byte0 = tmp[i]; // 取第 i 个字节 str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换, // >>> 为逻辑右移,将符号位一起右移 str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换 } s = new String(str); // 换后的结果转换为字符串 return s; } /** * * @param filePath * @param fileExtension * @param map */ private static void getHtml(String filePath,String fileExtension,Map map){ readHtmlFile(fileScanDirec,".ftl",map); } /** * 得到路径下的所需文件和文件的md5值 * @param filePath * @param fileExtension * @param map */ private static void readHtmlFile(String filePath,String fileExtension,Map map){ File file = new File(filePath); if(file.isFile()){ String fiePath = file.getAbsolutePath(); //符合指定类型 if(fiePath.endsWith(fileExtension)){ addVersion(file,map); } }else if(file.isDirectory()){ String[] fileList = file.list(); for(String name : fileList){ readHtmlFile(filePath + File.separator + name,fileExtension,map); } } } /** * 得到一个文件的map * @param file * @param map */ private static void addVersion(File file,Map map){ //TODO 缩短md5值 InputStream is = null; OutputStream os = null; List contentList = new ArrayList(); // 读文件 try { //指定utf-8格式 BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8")); String line = null; while ((line = br.readLine()) != null) { //TODO version 传null值 String modLine = getModLine(line, null,map); if (modLine != null) { //System.out.println(modLine); line = modLine; } line = line + "\r\n"; contentList.add(line); } // 关闭流 br.close(); } catch (Exception e) { System.out.println("读文件失败:" + file.getAbsolutePath()); e.printStackTrace(); } finally { if (null != is) { try { is.close(); } catch (Exception e) { e.printStackTrace(); } } } // 写文件 try { //指定utf8格式 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"utf-8")); for (Iterator it = contentList.iterator(); it.hasNext();) { String line = it.next(); bw.write(line); } // 更新到文件 bw.flush(); // 关闭流 bw.close(); } catch (Exception e) { System.out.println("写文件失败:" + file.getAbsolutePath()); e.printStackTrace(); } finally { if (null != os) { try { os.close(); } catch (Exception e) { e.printStackTrace(); } } } } /** * * 查找文件需要的版本号的js 与css 行 * @param line 行字符 * @param version 默认版本 * @param map md5 map * @return */ private static String getModLine(String line, String version, Map map) { //匹配html引入js文件 eg: if (scriptImportPattrrn.matcher(line).find()) { //以引号分隔,以/开始 String md5Version = null; String strLine[] = line.split("\""); for(String str:strLine){ if(null != md5Version){ break; } if(str.startsWith("/")){ //如果已有版本号,按?分隔,取第一个 if(str.lastIndexOf("?version") != -1){ str = str.substring(0, str.lastIndexOf("?version")); } md5Version = map.get(str); } } int pos = line.lastIndexOf(".js"); if(null != md5Version){ String modLine = line.substring(0, pos) + ".js?version=" + md5Version+ "\">"; return modLine; } //匹配html引入css文件 eg: } else if (cssImportPattrrn.matcher(line).find()) { //以引号分隔,以/开始 String md5Version = null; String strLine[] = line.split("\""); for(String str:strLine){ if(null != md5Version){ break; } if(str.startsWith("/")){ //如果已有版本号,按?分隔,取第一个 if(str.lastIndexOf("?version") != -1){ str = str.substring(0, str.lastIndexOf("?version")); } md5Version = map.get(str); } } int pos = line.lastIndexOf(".css"); if(null != md5Version){ String modLine = line.substring(0, pos) + ".css?version=" + md5Version+ "\" rel=\"stylesheet\">"; return modLine; } } return line; } /** * * @param args */ public static void main(String[] args) { Date date = new Date(); SimpleDateFormat df = new SimpleDateFormat("yyyyMMddhhmmss"); String version = df.format(date); Map objMap = new HashMap(); //添加js文件 getFileMD5Map(fileScanDirec,".js",objMap,version); //添加CSS文件 getFileMD5Map(fileScanDirec,".css",objMap,version); //循环html文件,修改其中的css和js引入路径 getHtml(fileScanDirec,".ftl",objMap); System.out.println("successful"); } }