2010-10-13 12 views
158

Estoy buscando una forma de obtener una lista de todos los nombres de recursos de un directorio de ruta de clases determinado, algo así como un método List<String> getResourceNames (String directoryName).Obtener una lista de recursos del directorio classpath

Por ejemplo, dado un directorio de ruta de clases x/y/z archivos que contienen a.html, b.html, c.html y un subdirectorio d, getResourceNames("x/y/z") debería devolver un List<String> que contiene las siguientes cadenas: ['a.html', 'b.html', 'c.html', 'd'].

Debería funcionar tanto para los recursos del sistema de archivos como para los archivos jar.

Sé que puedo escribir un fragmento rápido con File s, JarFile sy URL s, pero no quiero reinventar la rueda. Mi pregunta es, dadas las bibliotecas públicas disponibles, ¿cuál es la forma más rápida de implementar getResourceNames? Las pilas de Spring y Apache Commons son factibles.

+1

Respuesta relacionada: http://stackoverflow.com/a/30149061/4102160 – Cfx

Respuesta

121

escáner personalizada

Implemente su propio escáner. Por ejemplo:

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

Uso PathMatchingResourcePatternResolver de Spring Framework.

Ronmamo Reflexiones

Las otras técnicas podría ser lenta en tiempo de ejecución para valores grandes de CLASSPATH. Una solución más rápida es usar ronmamo's Reflections API, que precompila la búsqueda en tiempo de compilación.

+0

Reflexiones parece una gran idea, pero sin integración IDE es un poco limitante. – JBCP

+7

si usa Reflections, todo lo que realmente necesita: 'new Reflections (" my.package ", new ResourcesScanner()). GetResources (pattern)' – zapp

+8

¿Funciona la primera solución cuando se ejecuta desde el archivo jar? Para mí, no es así. Puedo leer el archivo de esta manera, pero no puedo leer el directorio. –

44

Este es el código
Fuente: forums.devx.com/showthread.php?t=153784

import 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); 
     } 
    } 
} 

Si está utilizando la primavera Tenga una mirada en PathMatchingResourcePatternResolver

+3

Interlocutor sabe cómo implementarlo usando clases estándar de Java, pero él quiere saber cómo puede utilizar bibliotecas existentes (Spring, Apache Commons). –

+2

'PathMatchingResourcePatternResolver' hizo el truco. – viaclectic

+0

@Jeroen Rosenberg También hay otra forma de que se haya seleccionado finalmente :) –

14

Si utiliza Apache commonsIO puede utilizar para el sistema de archivos (opcionalmente con filtro de extensión):

Collection<File> files = 
    FileUtils.listFiles(new File("directory/"), null, false); 

y de recursos/ruta de clases:

List<String> files = IOUtils.readLines(MyClass.class.getClassLoader() 
     .getResourceAsStream("directory/"), Charsets.UTF_8); 

Si usted no sabe si " directoy /" está en el sistema de archivos o en los recursos que podría añadir un

if(new File("directory/").isDirectory()) 

o

if(MyClass.class.getClassLoader() 
     .getResource("directory/") != null) 

antes de que las llamadas y usar ambos en combinación ...

+14

Files! = Resources – djechlin

+3

Claro, los recursos pueden no ser siempre archivos, pero la pregunta era sobre recursos que provienen de archivos/directorios. Entonces, use el código de ejemplo si desea verificar una ubicación diferente, p. si tiene un config.xml en sus recursos para alguna configuración predeterminada y debería ser posible cargar un config.xml externo en su lugar si existe ... – Rob

+1

¿Y por qué los recursos no son archivos? Los recursos son archivos archivados en un archivo zip. Esos se cargan en la memoria y se accede de forma específica, pero no veo por qué no son archivos. ¿Algún exaple de recurso que no es 'un archivo dentro de archivo'? –

5

Así que en términos de la PathMatchingResourcePatternResolver esto es lo que se necesita en el código:

@Autowired 
ResourcePatternResolver resourceResolver; 

public void getResources() 
{ 
    resourceResolver.getResources("classpath:config/*.xml"); 
} 
4

Se utiliza una combinación de la respuesta de Rob .

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 
} 
10

Uso de Google Reflexiones:

Reflections reflections = new Reflections(null, new ResourcesScanner()); 
Set<String> resourceList = reflections.getResources(x -> true); 

Otra muestra: obtener todos los archivos con la extensión .csv de some.package:

Reflections reflections = new Reflections("some.package", new ResourcesScanner()); 
Set<String> fileNames = reflections.getResources(Pattern.compile(".*\\.csv")); 
-3

Basado en @ Rob 's La información anterior, creé la implementación que estoy lanzando al dominio público:

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 
} 

Si bien no esperaba que getResourcesAsStream funcionara así en un directorio, realmente funciona y funciona bien.

+4

no lee el InputStream –

0

El Spring framework 's PathMatchingResourcePatternResolver es realmente impresionante para estas cosas:

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 dependencia:

<dependency> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-core</artifactId> 
    <version>LATEST</version> 
</dependency> 
0

Esto debería funcionar (si la primavera no es una opción):

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; 
} 
Cuestiones relacionadas