Получить список ресурсов из каталога классов
Я ищу способ получить список всех имен ресурсов из данного каталога classpath, что-то вроде метода List<String> getResourceNames (String directoryName)
.
например, учитывая каталог classpath x/y/z
содержащие файлы a.html
,b.html
,c.html
и подкаталог d
,getResourceNames("x/y/z")
возвращает List<String>
содержит следующие строки:['a.html', 'b.html', 'c.html', 'd']
.
он должен работать как для ресурсов в файловой системе, так и для jars.
Я знаю, что могу написать быстрый фрагмент с File
s,JarFile
s и URL
s,но я не хочу изобретать велосипед. Мой вопрос, учитывая существующие общедоступные библиотеки, каков самый быстрый способ реализации getResourceNames
? Spring и Apache Commons стеки оба возможны.
9 ответов:
Пользовательские Сканер
реализовать свой собственный сканер. Например:
private List<String> getResourceFiles( String path ) throws IOException { List<String> filenames = new ArrayList<>(); try( InputStream in = getResourceAsStream( path ); BufferedReader br = new BufferedReader( new InputStreamReader( in ) ) ) { String resource; while( (resource = br.readLine()) != null ) { filenames.add( resource ); } } return filenames; } private InputStream getResourceAsStream( String resource ) { final InputStream in = getContextClassLoader().getResourceAsStream( resource ); return in == null ? getClass().getResourceAsStream( resource ) : in; } private ClassLoader getContextClassLoader() { return Thread.currentThread().getContextClassLoader(); }
Spring Framework
использовать
PathMatchingResourcePatternResolver
С весны рамки.Ronmamo Размышления
другие методы могут быть медленными во время выполнения для огромных значений пути к классам. Более быстрое решение-использовать ronmamo Reflections API, который предварительно компилирует поиск во время компиляции.
здесь код
источник: forums.devx.com/showthread.php?t=153784import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; /** * list resources available from the classpath @ * */ public class ResourceList{ /** * for all elements of java.class.path get a Collection of resources Pattern * pattern = Pattern.compile(".*"); gets all resources * * @param pattern * the pattern to match * @return the resources in the order they are found */ public static Collection<String> getResources( final Pattern pattern){ final ArrayList<String> retval = new ArrayList<String>(); final String classPath = System.getProperty("java.class.path", "."); final String[] classPathElements = classPath.split(System.getProperty("path.separator")); for(final String element : classPathElements){ retval.addAll(getResources(element, pattern)); } return retval; } private static Collection<String> getResources( final String element, final Pattern pattern){ final ArrayList<String> retval = new ArrayList<String>(); final File file = new File(element); if(file.isDirectory()){ retval.addAll(getResourcesFromDirectory(file, pattern)); } else{ retval.addAll(getResourcesFromJarFile(file, pattern)); } return retval; } private static Collection<String> getResourcesFromJarFile( final File file, final Pattern pattern){ final ArrayList<String> retval = new ArrayList<String>(); ZipFile zf; try{ zf = new ZipFile(file); } catch(final ZipException e){ throw new Error(e); } catch(final IOException e){ throw new Error(e); } final Enumeration e = zf.entries(); while(e.hasMoreElements()){ final ZipEntry ze = (ZipEntry) e.nextElement(); final String fileName = ze.getName(); final boolean accept = pattern.matcher(fileName).matches(); if(accept){ retval.add(fileName); } } try{ zf.close(); } catch(final IOException e1){ throw new Error(e1); } return retval; } private static Collection<String> getResourcesFromDirectory( final File directory, final Pattern pattern){ final ArrayList<String> retval = new ArrayList<String>(); final File[] fileList = directory.listFiles(); for(final File file : fileList){ if(file.isDirectory()){ retval.addAll(getResourcesFromDirectory(file, pattern)); } else{ try{ final String fileName = file.getCanonicalPath(); final boolean accept = pattern.matcher(fileName).matches(); if(accept){ retval.add(fileName); } } catch(final IOException e){ throw new Error(e); } } } return retval; } /** * list the resources that match args[0] * * @param args * args[0] is the pattern to match, or list all resources if * there are no args */ public static void main(final String[] args){ Pattern pattern; if(args.length < 1){ pattern = Pattern.compile(".*"); } else{ pattern = Pattern.compile(args[0]); } final Collection<String> list = ResourceList.getResources(pattern); for(final String name : list){ System.out.println(name); } } }
Если вы используете Spring, посмотрите на PathMatchingResourcePatternResolver
если вы используете apache commonsIO, вы можете использовать для файловой системы (необязательно с фильтром расширения):
Collection<File> files = FileUtils.listFiles(new File("directory/"), null, false);
и для ресурсов / classpath:
List<String> files = IOUtils.readLines(MyClass.class.getClassLoader() .getResourceAsStream("directory/"), Charsets.UTF_8);
если вы не знаете, находится ли "directoy/" в файловой системе или в ресурсах, вы можете добавить
if( new File("directory/").isDirectory() )
или
if( MyClass.class.getClassLoader() .getResource("directory/") != null )
перед вызовами и использовать в сочетании...
Использование Google Reflections:
Reflections reflections = new Reflections(null, new ResourcesScanner()); Set<String> resourceList = reflections.getResources(x -> true);
другой пример: получить все файлы с .csv
Итак, с точки зрения PathMatchingResourcePatternResolver это то, что нужно в коде:
@Autowired ResourcePatternResolver resourceResolver; public void getResources() { resourceResolver.getResources("classpath:config/*.xml"); }
использовал комбинацию ответа Роба.
final String resourceDir = "resourceDirectory/"; List<String> files = IOUtils.readLines(Thread.currentThread.getClass().getClassLoader().getResourceAsStream(resourceDir), Charsets.UTF_8); for(String f : files){ String data= IOUtils.toString(Thread.currentThread.getClass().getClassLoader().getResourceAsStream(resourceDir + f)); ....process data }
The
Spring framework
' sPathMatchingResourcePatternResolver
действительно потрясающе для этих вещей:private Resource[] getXMLResources() throws IOException { ClassLoader classLoader = MethodHandles.lookup().getClass().getClassLoader(); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader); return resolver.getResources("classpath:x/y/z/*.xml"); }
зависимостей Maven:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>LATEST</version> </dependency>
Это должно работать (если spring не является опцией):
public static List<String> getFilenamesForDirnameFromCP(String directoryName) throws URISyntaxException, UnsupportedEncodingException, IOException { List<String> filenames = new ArrayList<String>(); URL url = Thread.currentThread().getContextClassLoader().getResource(directoryName); if (url != null) { if (url.getProtocol().equals("file")) { File file = Paths.get(url.toURI()).toFile(); if (file != null) { File[] files = file.listFiles(); if (files != null) { for (File filename : files) { filenames.add(filename.toString()); } } } } else if (url.getProtocol().equals("jar")) { String dirname = directoryName + "/"; String path = url.getPath(); String jarPath = path.substring(5, path.indexOf("!")); try (JarFile jar = new JarFile(URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name()))) { Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); if (name.startsWith(dirname) && !dirname.equals(name)) { URL resource = Thread.currentThread().getContextClassLoader().getResource(name); filenames.add(resource.toString()); } } } } } return filenames; }
основываясь на информации @rob выше, я создал реализацию, которую я выпускаю в общественное достояние:
private static List<String> getClasspathEntriesByPath(String path) throws IOException { InputStream is = Main.class.getClassLoader().getResourceAsStream(path); StringBuilder sb = new StringBuilder(); while (is.available()>0) { byte[] buffer = new byte[1024]; sb.append(new String(buffer, Charset.defaultCharset())); } return Arrays .asList(sb.toString().split("\n")) // Convert StringBuilder to individual lines .stream() // Stream the list .filter(line -> line.trim().length()>0) // Filter out empty lines .collect(Collectors.toList()); // Collect remaining lines into a List again }
хотя я бы и не ожидал
getResourcesAsStream
чтобы работать так в каталоге, он действительно работает, и он хорошо работает.