package org.nutz.lang.util; import java.io.File; import java.io.FileFilter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.util.LinkedList; import org.nutz.lang.Encoding; import org.nutz.lang.Files; import org.nutz.lang.Lang; import org.nutz.lang.Strings; /** * 磁盘操作的帮助函数集合 * * @author zozoh(zozohtnt@gmail.com) * @author bonyfish(mc02cxj@gmail.com) */ public abstract class Disks { /** * 一个 Vistor 模式的目录深层遍历 * * @param f * 要遍历的目录或者文件,如果是目录,深层遍历,否则,只访问一次文件 * @param fv * 对文件要进行的操作 * @param filter * 遍历目录时,哪些文件应该被忽略 * @return 遍历的文件个数 */ public static int visitFile(File f, FileVisitor fv, FileFilter filter) { int re = 0; if (f.isFile()) { fv.visit(f); re++; } else if (f.isDirectory()) { File[] fs = null == filter ? f.listFiles() : f.listFiles(filter); if (fs != null) for (File theFile : fs) re += visitFile(theFile, fv, filter); } return re; } /** * 将两个文件对象比较,得出相对路径 * * @param base * 基础文件对象 * @param file * 相对文件对象 * @return 相对于基础文件对象的相对路径 */ public static String getRelativePath(File base, File file) { String pathBase = base.getAbsolutePath(); if (base.isDirectory()) pathBase += "/"; String pathFile = file.getAbsolutePath(); if (file.isDirectory()) pathFile += "/"; return getRelativePath(pathBase, pathFile); } /** * 将两个路径比较,得出相对路径 * * @param base * 基础路径,以 '/' 结束,表示目录 * @param path * 相对文件路径,以 '/' 结束,表示目录 * @return 相对于基础路径对象的相对路径 */ public static String getRelativePath(String base, String path) { String[] bb = Strings.splitIgnoreBlank(getCanonicalPath(base), "[\\\\/]"); String[] ff = Strings.splitIgnoreBlank(getCanonicalPath(path), "[\\\\/]"); int len = Math.min(bb.length, ff.length); int pos = 0; for (; pos < len; pos++) if (!bb[pos].equals(ff[pos])) break; if (len == pos && bb.length == ff.length) return "./"; int dir = 1; if (base.endsWith("/")) dir = 0; StringBuilder sb = new StringBuilder(Strings.dup("../", bb.length - pos - dir)); return sb.append(Lang.concat(pos, ff.length - pos, '/', ff)).toString(); } /** * 整理路径。 将会合并路径中的 ".." * * @param path * 路径 * @return 整理后的路径 */ public static String getCanonicalPath(String path) { if (Strings.isBlank(path)) return path; String[] pa = Strings.splitIgnoreBlank(path, "[\\\\/]"); LinkedList<String> paths = new LinkedList<String>(); for (String s : pa) { if ("..".equals(s)) { if (paths.size() > 0) paths.removeLast(); continue; } else { paths.add(s); } } if (path.charAt(0) == '/') return Lang.concat("/", paths).insert(0, '/').toString(); return Lang.concat("/", paths).toString(); } /** * @return 当前账户的主目录全路径 */ public static String home() { return System.getProperty("user.home"); } /** * @param path * 相对用户主目录的路径 * @return 相对用户主目录的全路径 */ public static String home(String path) { return home() + path; } /** * 获取一个路径的绝对路径。如果该路径不存在,则返回null * * @param path * 路径 * @return 绝对路径 */ public static String absolute(String path) { return absolute(path, ClassTools.getClassLoader(), Encoding.defaultEncoding()); } /** * 获取一个路径的绝对路径。如果该路径不存在,则返回null * * @param path * 路径 * @param klassLoader * 参考 ClassLoader * @param enc * 路径编码方式 * @return 绝对路径 */ public static String absolute(String path, ClassLoader klassLoader, String enc) { path = normalize(path, enc); if (Strings.isEmpty(path)) return null; File f = new File(path); if (!f.exists()) { URL url = null; try { url = klassLoader.getResource(path); if (null == url) url = Thread.currentThread() .getContextClassLoader() .getResource(path); if (null == url) url = ClassLoader.getSystemResource(path); } catch (Throwable e) {} if (null != url) return normalize(url.getPath(), Encoding.UTF8);// 通过URL获取String,一律使用UTF-8编码进行解码 return null; } return path; } /** * 让路径变成正常路径,将 ~ 替换成用户主目录 * * @param path * 路径 * @return 正常化后的路径 */ public static String normalize(String path) { return normalize(path, Encoding.defaultEncoding()); } /** * 让路径变成正常路径,将 ~ 替换成用户主目录 * * @param path * 路径 * @param enc * 路径编码方式 * @return 正常化后的路径 */ public static String normalize(String path, String enc) { if (Strings.isEmpty(path)) return null; if (path.charAt(0) == '~') path = Disks.home() + path.substring(1); try { return URLDecoder.decode(path, enc); } catch (UnsupportedEncodingException e) { return null; } } /** * 遍历文件夹下以特定后缀结尾的文件(不包括文件夹,不包括.开头的文件) * * @param path * 根路径 * @param regex * 文件名的正则表达式 * @param deep * 是否深层遍历 * @param fv * 你所提供的访问器,当然就是你自己的逻辑咯 */ public static final void visitFile(String path, final String regex, final boolean deep, final FileVisitor fv) { File d = Files.findFile(path); if (null == d) return; visitFile(d, new FileVisitor() { public void visit(File f) { if (f.isDirectory()) return; fv.visit(f); } }, new FileFilter() { public boolean accept(File f) { if (f.isDirectory()) return deep; if (f.isHidden()) return false; if (Strings.isEmpty(regex)) return true; return f.getName().matches(regex); } }); } }