web-dev-qa-db-fra.com

Obtenir une liste des ressources du répertoire classpath

Je cherche un moyen d'obtenir une liste de tous les noms de ressources d'un répertoire de chemin de classe donné, quelque chose comme une méthode List<String> getResourceNames (String directoryName).

Par exemple, étant donné un répertoire de chemin de classe x/y/z contenant des fichiers a.html, b.html, c.html et un sous-répertoire d, getResourceNames("x/y/z") devrait renvoyer un List<String> contenant les chaînes suivantes: ['a.html', 'b.html', 'c.html', 'd'].

Cela devrait fonctionner aussi bien pour les ressources dans le système de fichiers que pour les fichiers JAR.

Je sais que je peux écrire un extrait avec Files, JarFiles et URLs, mais je ne veux pas réinventer la roue. Ma question est, étant donné les bibliothèques existantes publiquement disponibles, quel est le moyen le plus rapide de mettre en œuvre getResourceNames? Les piles Spring et Apache Commons sont toutes deux réalisables.

219
viaclectic

Scanner Personnalisé

Implémentez votre propre scanner. Par exemple:

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

Cadre de printemps

Utilisez PathMatchingResourcePatternResolver à partir de Spring Framework.

Ronmamo Reflections

Les autres techniques peuvent être lentes au moment de l'exécution pour des valeurs CLASSPATH énormes. Une solution plus rapide consiste à utiliser Reflections API de ronmamo, qui précompile la recherche au moment de la compilation.

154
iirekm

Voici le code
Source : 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 vous utilisez Spring Regardez PathMatchingResourcePatternResolver

47
Jigar Joshi

Utilisation de Google Reflections:

Obtenez tout sur le classpath:

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

Un autre exemple - récupère tous les fichiers avec l’extension . Csv de some.package:

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

Si vous utilisez Apache commonsIO, vous pouvez utiliser pour le système de fichiers (éventuellement avec filtre d’extension):

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

et pour les ressources/classpath:

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

Si vous ne savez pas si "directoy /" est dans le système de fichiers ou dans les ressources, vous pouvez ajouter un

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

ou

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

avant les appels et utiliser les deux en combinaison ...

16
Rob

Donc, en termes de PathMatchingResourcePatternResolver, voici ce dont le code a besoin:

@Autowired
ResourcePatternResolver resourceResolver;

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

Le PathMatchingResourcePatternResolver de Spring framework est vraiment génial pour ces choses:

private Resource[] getXMLResources() throws IOException
{
    ClassLoader classLoader = MethodHandles.lookup().getClass().getClassLoader();
    PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);

    return resolver.getResources("classpath:x/y/z/*.xml");
}

Dépendance Maven:

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

Utilisé une combinaison de la réponse 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
}
5
Trevor

Cela devrait fonctionner (si le printemps n'est pas une option):

public static List<String> getFilenamesForDirnameFromCP(String directoryName) throws URISyntaxException, UnsupportedEncodingException, IOException {
    List<String> filenames = new ArrayList<>();

    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;
}
3
fl0w

Avec le printemps, c'est facile. Que ce soit un fichier, un dossier ou même plusieurs fichiers, il y a des chances, vous pouvez le faire par injection.

Cet exemple illustre l'injection de plusieurs fichiers situés dans le dossier x/y/z.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

@Service
public class StackoverflowService {
    @Value("classpath:x/y/z/*")
    private Resource[] resources;

    public List<String> getResourceNames() {
        return Arrays.stream(resources)
                .map(Resource::getFilename)
                .collect(Collectors.toList());
    }
}

Cela fonctionne pour les ressources dans le système de fichiers ainsi que dans les JAR.

1
naXa

Aucune des réponses n'a fonctionné pour moi même si mes ressources ont été placées dans des dossiers de ressources et que j'ai suivi les réponses ci-dessus. Ce qui a fait un tour était:

@Value("file:*/**/resources/**/schema/*.json")
private Resource[] resources;
0
kukis

Je pense que vous pouvez utiliser [fournisseur de système de fichiers Zip] [1] pour y parvenir. Lorsque vous utilisez FileSystems.newFileSystem, il semble que vous puissiez traiter les objets de ce fichier Zip comme un fichier "normal".

Dans la documentation liée ci-dessus:

Spécifiez les options de configuration du système de fichiers Zip dans l'objet Java.util.Map transmis à la méthode FileSystems.newFileSystem. Reportez-vous à la rubrique [Propriétés du système de fichiers Zip] [2] pour plus d'informations sur les propriétés de configuration spécifiques au fournisseur du système de fichiers Zip.

Une fois que vous avez une instance d’un système de fichiers Zip, vous pouvez appeler les méthodes des classes [Java.nio.file.FileSystem] [3] et [Java.nio.file.Path] [4] pour effectuer des opérations telles que la copie, le déplacement et la copie. renommer des fichiers, ainsi que modifier les attributs de fichier.

La documentation du module jdk.zipfs dans [états de Java 11] [5]:

Le fournisseur de système de fichiers Zip considère un fichier Zip ou JAR comme un système de fichiers et offre la possibilité de manipuler le contenu du fichier. Le fournisseur de système de fichiers Zip peut être créé par [FileSystems.newFileSystem] [6] s'il est installé.

Voici un exemple artificiel que j'ai utilisé avec vos exemples de ressources. Notez qu'un .Zip est un .jar, mais vous pouvez adapter votre code pour utiliser à la place les ressources classpath:

Configuration

cd /tmp
mkdir -p x/y/z
touch x/y/z/{a,b,c}.html
echo 'hello world' > x/y/z/d
Zip -r example.Zip x

Java

import Java.io.IOException;
import Java.net.URI;
import Java.nio.file.FileSystem;
import Java.nio.file.FileSystems;
import Java.nio.file.Files;
import Java.util.Collections;
import Java.util.stream.Collectors;

public class MkobitZipRead {

  public static void main(String[] args) throws IOException {
    final URI uri = URI.create("jar:file:/tmp/example.Zip");
    try (
        final FileSystem zipfs = FileSystems.newFileSystem(uri, Collections.emptyMap());
    ) {
      Files.walk(zipfs.getPath("/")).forEach(path -> System.out.println("Files in Zip:" + path));
      System.out.println("-----");
      final String manifest = Files.readAllLines(
          zipfs.getPath("x", "y", "z").resolve("d")
      ).stream().collect(Collectors.joining(System.lineSeparator()));
      System.out.println(manifest);
    }
  }

}

sortie

Files in Zip:/
Files in Zip:/x/
Files in Zip:/x/y/
Files in Zip:/x/y/z/
Files in Zip:/x/y/z/c.html
Files in Zip:/x/y/z/b.html
Files in Zip:/x/y/z/a.html
Files in Zip:/x/y/z/d
-----
hello world
0
mkobit