001package net.gdface.utils; 002 003import java.io.File; 004import java.io.FileNotFoundException; 005import java.io.FilenameFilter; 006import java.io.IOException; 007import java.io.InputStream; 008import java.net.URI; 009import java.net.URL; 010import java.net.URLDecoder; 011import java.util.ArrayList; 012import java.util.Arrays; 013import java.util.Enumeration; 014import java.util.Iterator; 015import java.util.LinkedList; 016import java.util.List; 017import java.util.jar.JarEntry; 018import java.util.jar.JarFile; 019 020/** 021 * classpath resource 工具 022 * @author guyadong 023 * 024 */ 025public class ClassResourceUtils { 026 027 /** 028 * 文件åè¿‡æ»¤å™¨æŽ¥å£ 029 * @author guyadong 030 * 031 */ 032 public static interface FileFilter{ 033 /** 034 * Tests if a specified file should be included in a file list. 035 * @param filename 036 * @return 037 */ 038 boolean accept(String filename); 039 } 040 /** 041 * å‚è§ {@link #getResourceFileList(Class, String)},<br> 042 * {@link IOException}å°è£…为{@link RuntimeException}, 043 * @param clazz Class to use when getting the System classloader 044 * @param path 045 * @return 046 */ 047 public static List<String> getFilesUnchedked(Class<?> clazz, String path){ 048 try { 049 return getResourceFileList(ClassResourceUtils.class,path); 050 } catch (IOException e) { 051 throw new RuntimeException(e); 052 } 053 } 054 /** 055 * 056 * @param clazz Class to use when getting the System classloader 057 * @param path 058 * @param filter A filename filter 059 * @return 060 * @see #getFilesUnchedked(Class, String) 061 */ 062 public static List<String> getFilesUnchecked(Class<?> clazz,String path, FileFilter filter){ 063 List<String> list = getFilesUnchedked(clazz, path); 064 if(null != filter){ 065 for(Iterator<String> itor = list.iterator();itor.hasNext();){ 066 String file = itor.next(); 067 if(!filter.accept(file)){ 068 itor.remove(); 069 } 070 } 071 } 072 return list; 073 } 074 /** 075 * Returns true if resource exist. 076 * @param clazz Class to use when getting the System classloader 077 * @param resource 078 * @return 079 */ 080 public static boolean resourceExist(Class<?> clazz, String resource){ 081 return getResource(clazz,resource)!=null; 082 } 083 084 /** 085 * Return a context-relative path, beginning with a "/", that represents 086 * the canonical version of the specified path after ".." and "." elements 087 * are resolved out. If the specified path attempts to go outside the 088 * boundaries of the current context (i.e. too many ".." path elements 089 * are present), return <code>null</code> instead. 090 * 091 * @param path Path to be normalized 092 * @return String normalized path 093 */ 094 public static String normalizePath(String path) 095 { 096 // Normalize the slashes and add leading slash if necessary 097 String normalized = path; 098 if (normalized.indexOf('\\') >= 0) 099 { 100 normalized = normalized.replace('\\', '/'); 101 } 102 103 if (!normalized.startsWith("/")) 104 { 105 normalized = "/" + normalized; 106 } 107 108 // Resolve occurrences of "//" in the normalized path 109 while (true) 110 { 111 int index = normalized.indexOf("//"); 112 if (index < 0){ 113 break; 114 } 115 normalized = normalized.substring(0, index) + 116 normalized.substring(index + 1); 117 } 118 119 // Resolve occurrences of "%20" in the normalized path 120 while (true) 121 { 122 int index = normalized.indexOf("%20"); 123 if (index < 0){ 124 break; 125 } 126 normalized =new StringBuilder() 127 .append(normalized.substring(0, index)) 128 .append(" ") 129 .append(normalized.substring(index + 3)).toString(); 130 } 131 132 // Resolve occurrences of "/./" in the normalized path 133 while (true) 134 { 135 int index = normalized.indexOf("/./"); 136 if (index < 0){ 137 break; 138 } 139 normalized = normalized.substring(0, index) + 140 normalized.substring(index + 2); 141 } 142 143 // Resolve occurrences of "/../" in the normalized path 144 while (true) 145 { 146 int index = normalized.indexOf("/../"); 147 if (index < 0){ 148 break; 149 } 150 if (index == 0){ 151 // Trying to go outside our context 152 return (null); 153 } 154 int index2 = normalized.lastIndexOf('/', index - 1); 155 normalized = normalized.substring(0, index2) + 156 normalized.substring(index + 3); 157 } 158 159 // Return the normalized path that we have completed 160 return (normalized); 161 } 162 /** 163 * 164 * Return a normalized directory path, end with "/", but not start with one 165 * @param path Path to be normalized 166 * @return Path to be normalized 167 * @see #normalizePath(String) 168 */ 169 public static String normalizeDirPath(String path){ 170 path = normalizePath(path); 171 if (path.startsWith("/")) 172 { 173 path = path.substring(1); 174 } 175 if(!path.endsWith("/")){ 176 path += "/"; 177 } 178 return path; 179 } 180 /** 181 * Returns an input stream for reading the specified resource. 182 * @param clazz Class to use when getting the System classloader (used if no Thread 183 * Context classloader available or fails to get resource). 184 * @param name name of the resource 185 * @return InputStream for the resource. 186 * @see #getResource(Class, String) 187 */ 188 public static InputStream getResourceAsStream(Class<?> clazz, String name) 189 { 190 URL url = getResource(clazz,name); 191 try { 192 return url != null ? url.openStream() : null; 193 } catch (IOException e) { 194 return null; 195 } 196 } 197 /** 198 * Finds a resource with the given name. Checks the Thread Context 199 * classloader, then uses the System classloader. Should replace all 200 * calls to <code>Class.getResourceAsString</code> when the resource 201 * might come from a different classloader. (e.g. a webapp). 202 * @param claz Class to use when getting the System classloader (used if no Thread 203 * Context classloader available or fails to get resource). 204 * @param path name of the resource 205 * @return A <tt>URL</tt> object for reading the resource, or 206 * <tt>null</tt> if the resource could not be found or the invoker 207 * doesn't have adequate privileges to get the resource. 208 */ 209 public static URL getResource(Class<?> claz, String path) 210 { 211 URL result = null; 212 path = normalizePath(path); 213 /** 214 * remove leading slash so path will work with classes in a JAR file 215 */ 216 path = path.substring(1); 217 ClassLoader classLoader = Thread.currentThread() 218 .getContextClassLoader(); 219 220 if (classLoader == null) 221 { 222 classLoader = claz.getClassLoader(); 223 result = classLoader.getResource( path ); 224 } 225 else 226 { 227 result= classLoader.getResource( path ); 228 229 /** 230 * for compatibility with texen / ant tasks, fall back to 231 * old method when resource is not found. 232 */ 233 234 if (result == null) 235 { 236 classLoader = claz.getClassLoader(); 237 if (classLoader != null){ 238 result = classLoader.getResource( path ); 239 } 240 } 241 } 242 return result; 243 244 } 245 private static final FilenameFilter FILE_FILTER = new FilenameFilter(){ 246 @Override 247 public boolean accept(File dir, String name) { 248 return new File(dir,name).isFile(); 249 }}; 250 private static final String PROTOCOL_FILE = "file"; 251 private static final String PROTOCOL_JAR = "jar"; 252 /** 253 * List file names for a resource folder. Not recursive. 254 * This is basically a brute-force implementation. 255 * Works for regular files and also JARs.<br> 256 * refer to: <a href="http://www.uofr.net/~greg/java/get-resource-listing.html">Java: Listing the contents of a resource directory</a> 257 * @param clazz Any java class that lives in the same place as the resources you want. 258 * @param dirPath 259 * @return Just the name of each member item, not the full paths. 260 * @throws IOException 261 */ 262 public static List<String> getResourceFileList(Class<?> clazz, String dirPath) throws IOException { 263 if(null == dirPath || dirPath.isEmpty()){ 264 throw new IllegalArgumentException("path must not be null or empty"); 265 } 266 dirPath = normalizeDirPath(dirPath); 267 URL dirURL = getResource(clazz,dirPath); 268 if(null == dirURL){ 269 throw new FileNotFoundException(dirPath); 270 } 271 272 if (PROTOCOL_FILE.equals(dirURL.getProtocol())) { 273 /* A file path: easy enough */ 274 return new ArrayList<String>(Arrays.asList(new File(URI.create(dirURL.toString())).list(FILE_FILTER))); 275 } 276 277 if (PROTOCOL_JAR.equals(dirURL.getProtocol())) { 278 /* A JAR path */ 279 //strip out only the JAR file 280 String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); 281 JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8")); 282 LinkedList<String> result = new LinkedList<String>(); 283 try{ 284 //gives ALL entries in jar 285 Enumeration<JarEntry> entries = jar.entries(); 286 while(entries.hasMoreElements()) { 287 JarEntry entry = entries.nextElement(); 288 // if it is a subdirectory, skip 289 if(!entry.isDirectory()){ 290 String name = entry.getName(); 291 if (name.startsWith(dirPath)) { 292 //filter according to the path 293 String element = name.substring(dirPath.length()); 294 int checkSubdir = element.indexOf("/"); 295 if (checkSubdir < 0 ) { 296 result.add(element); 297 } 298 } 299 } 300 } 301 }finally{ 302 jar.close(); 303 } 304 return result; 305 } 306 throw new UnsupportedOperationException("Cannot list files for URL "+dirURL); 307 } 308}