package com.guanghua.brick.util; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.jar.JarFile; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * @author leeon * 对类路径上的文件或者文件系统上的文件进行搜索的工具类 */ public class FileUtil { private static Log logger = LogFactory.getLog(FileUtil.class); /** * 在文件系统的某个目录中搜索符合pattern要求的文件,filePath就是搜索的目录的全路径 * 比如filePath路径是c:/foo/bar,那么就是搜索c:/foo/bar下的所有符合patter格式的文件 * 如果filePath的不是目录或者不存在,那么返回一个空列表。 * @param filePath 需要搜索的目录路径 * @param pattern 文件名(不包括文件路径)符合的模版,正则表达式的模版 * @return 返回搜索到的文件全路径列表 */ public static List<String> searchFileFromFolder(String filePath, String pattern) { return searchFileFromFolder(getFileResource(filePath), pattern, null); } /** * 检查文件系统某个目录下所有名字(非路径,只包括文件名)符合正则表达式pattern要求的文件 * 返回的结果是一个String的列表,可以有两种结果 * 如果prefix为null,列表里的字符串就是符合要求的文件的绝对路径 * 如果prefix不为null,列表里的字符串就是类路径格式的字符串,比如在c:/foo/bar下搜索,prefix为foo1/bar1 * 搜索到一个文件是c:/foo/bar/test/Test.class,那么返回的list中的字符串就是foo1/bar1/test/Test.class * 如果prefix传的是"",那么返回的字符串就是/test/Test.class * @param folder, 开始搜索的文件夹,如果文件夹为空,或者不存在,或者不是文件夹,那么返回空列表 * @param pattern, 搜索用的正则表达式 * @param prefix, 返回类路径或者是文件绝对路径的标志,如果是null,返回文件绝对路径 * @return 返回list,符合list要求的每一个对象是该文件的绝对路径 */ public static List<String> searchFileFromFolder(File folder, final String pattern, String prefix) { List<String> re = new ArrayList<String>(); //给prefix加上/,以便于递归 if (prefix != null && prefix.length() != 0) prefix = prefix + "/"; //当文件夹存在,并且是文件夹时,才进行搜索 if (folder.exists() && folder.isDirectory()) { //获取符合pattern条件的文件列表 File[] files = folder.listFiles(new FileFilter() { public boolean accept(File file) { //如果该文件是隐藏的,不继续寻找 if (file.isHidden()) return false; else { //如果该文件是目录那么继续寻找他的儿子 if (file.isDirectory()) return true; else { //如果不是目录,则检验其文件名是否满足要求 return Pattern.matches(pattern, file .getName()); } } } }); //遍历文件列表,看是否是目录或者文件,是目录就递归,是文件就根据prefix返回 for (int i = 0; files != null && i < files.length; i++) { if (files[i].isDirectory()) { re.addAll(searchFileFromFolder(files[i], pattern, prefix == null ? null : prefix + files[i].getName())); } else { //需要返回文件路径 if (prefix == null) re.add(files[i].getAbsolutePath()); //需要返回类路径 else re.add(prefix + files[i].getName()); } } } return re; } /** * 检查zip包中某个目录下的所有文件名(不包括文件路径)符合正则表达式pattern要求的文件 * 返回一个文件类路径的列表. * folderPath是搜索的zip包中的类路径文件夹,prefix是一个其实搜索和返回路径的前缀,主要用于搜索war包时 * 比如一个war包,foo.war,那么zipFile时foo.war的ZipFile,要搜索pattern为*.hbm.xml,搜索的类路径就是com/guanghua/domain,而搜索的前缀就应该是/WEB-INF/classes * 这时候,真正开始搜索的zip包路径是/WEB-INF/classes/com/guanghua/domain,而返回的文件类路径却是com/guanghua/domain/Test.hbm.xml的形式,返回结果会自动截取前缀 * 同样在搜索ear包时,也同理可以这样做。搜索jar包的化,prefix传入""即可 * @param zipFile 被搜索的zip文件 * @param pattern 文件名必须符合的正则表达式的pattern * @param folderPath * @param prefix * @return zip文件夹中所有符合条件的文件的类路径列表 */ public static List<String> searchFileFromZip(ZipFile zipFile, String pattern, String folderPath, String prefix) { List<String> list = new ArrayList<String>(); //拿到zip中所有的entry Enumeration e = zipFile.entries(); folderPath = folderPath.startsWith("/") ? prefix + folderPath : prefix + "/" + folderPath; while (e.hasMoreElements()) { ZipEntry zipEntry = (ZipEntry) e.nextElement(); //获取zip中文件的路径 String zip = zipEntry.getName(); zip = zip.startsWith("/") ? zip : "/" + zip; //如果zipEntry的开头是prefix,那么说明这是prefix目录下的一个文件 if (!zipEntry.isDirectory() && zip.startsWith(folderPath)) { //获取真实的文件名 String jarFileName = zip .substring(zip.lastIndexOf("/") + 1); //文件名符合pattern的要求 if (Pattern.matches(pattern, jarFileName)) list.add(zip.substring(prefix.length() + 1)); } } return list; } /** * 在指定的类路径中搜索文件名符合pattern要求的文件 * 比如要搜索com/guanghua/brick下所有符合.hbm.xml结尾的文件就可以使用该方法 * 返回的结果是符合要求的文件的类路径,比如com/guanghua/brick/domain/foo.hbm.xml * @param classPath 指定的类路径 * @param pattern 文件名必须符合的pattern * @return 返回在指定类路径中文件名符合要求的类路径列表 * @throws Exception */ public static List<String> searchFileFromClassPath( String classPath, String pattern) throws IOException { //获取指定的类路径的定位URL URL[] urls = BeanUtil.getClassPathFileURLs(classPath); //返回的是list,但是利用set来保证没有重复的url List<String> list = new ArrayList<String>(); Set<String> set = new HashSet<String>(); //没有找到对应的url list,返回空 if (urls == null) return list; for (int i = 0; i < urls.length; i++) { //将搜索到的结果放入set set.addAll(searchFileFromClassPath(urls[i], classPath, pattern)); } list.addAll(set); return list; } /** * 根据URL确定搜索文件的路径,然后根据classPath确定搜索的类路径,再搜索文件名符合pattern格式的文件 * classPath所在的位置可能是jar,可能是war,可能是folder,但必须和url的位置一致。 * 在类路径中搜索符合pattern要求的文件 * @param url 搜索的jar包或者文件夹所在的url路径, * @param classPath 搜索的类路径 * @param pattern 文件名符合的模版 * @return 返回在指定类路径中文件名符合要求的类路径列表 * @throws IOException */ public static List<String> searchFileFromClassPath(URL url, String classPath, String pattern) throws IOException { logger.debug("search file type: [" + pattern + "]"); logger.debug("search file in classpath: [" + classPath + "]"); logger.debug("the real filepath is : [" + url + "]"); if (url == null) return new ArrayList<String>(); //处理文件名 String file = url.getFile(); int i = file.indexOf("!"); file = (i != -1) ? file.substring(0, i) : file; //获取协议 String protocol = url.getProtocol(); //如果类所在的文件路径是jar包 if ("jar".equals(protocol)) { JarURLConnection jc = (JarURLConnection) url .openConnection(); logger.debug("search jar file from :[" + url + "]"); return searchFileFromZip(jc.getJarFile(), pattern, classPath, ""); //如果类所在的文件路径是wsjar包 } else if ("wsjar".equals(protocol)) { //wsjar需要去掉file:/才能在ws下识别 if (file.startsWith("file:/")) file = file.substring(6); logger.debug("search wsjar file from :[" + file + "]"); JarFile jarFile = new JarFile(new File(URLDecoder.decode( file, "UTF-8"))); return searchFileFromZip(jarFile, pattern, classPath, ""); //如果类所在的文件路径是zip包 } else if ("zip".equals(protocol)) { //如果类所在的文件路径是war包 if (file.endsWith("war")) { logger.debug("search war file from :[" + file + "]"); ZipFile zipFile = new ZipFile(new File(URLDecoder .decode(file, "UTF-8"))); return searchFileFromZip(zipFile, pattern, classPath, "/WEB-INF/classes"); //如果类所在的文件路径是普通zip包 } else { logger.debug("search zip file from :[" + file + "]"); ZipFile zipFile = new ZipFile(new File(URLDecoder .decode(file, "UTF-8"))); return searchFileFromZip(zipFile, pattern, classPath, ""); } //如果是普通的文件协议 } else if ("file".equals(protocol)) { logger .debug("search filesystem folder from :[" + url + "]"); return searchFileFromFolder(new File(URLDecoder.decode(url .getFile(), "UTF-8")), pattern, classPath); //其余情况返回空列表 } else return new ArrayList<String>(); } /** * 根据传入文件完整路径,获取文件所在的文件夹路径 * 原理是截取最后一个"/"之前的字符串作为文件夹名称 * @param filePath 文件完整路径 * @return 文件夹名称 */ public static String getFloderName(String filePath) { return filePath.substring(0, filePath.lastIndexOf("/")); } /** * 根据传入的文件完整路径,获取文件的名称 * 原理是截取最后一个"/"之后的字符串作为文件名 * @param filePath 文件完整路径 * @return 文件名 */ public static String getFileName(String filePath) { return filePath.substring(filePath.lastIndexOf("/") + 1); } /** * 根据文件路径(绝对路径)直接返回文件对象。 * 相当于new File(filePath) * @param filePath 文件的绝对路径 * @return 文件对象 */ public static File getFileResource(String filePath) { return new File(filePath); } }