150

I have this code which reads all the files from a directory.

    File textFolder = new File("text_directory");

    File [] texFiles = textFolder.listFiles( new FileFilter() {
           public boolean accept( File file ) {
               return file.getName().endsWith(".txt");
           }
    });

It works great. It fills the array with all the files that end with ".txt" from directory "text_directory".

How can I read the contents of a directory in a similar fashion within a JAR file?

So what I really want to do is, to list all the images inside my JAR file, so I can load them with:

ImageIO.read(this.getClass().getResource("CompanyLogo.png"));

(That one works because the "CompanyLogo" is "hardcoded" but the number of images inside the JAR file could be from 10 to 200 variable length.)

EDIT

So I guess my main problem would be: How to know the name of the JAR file where my main class lives?

Granted I could read it using java.util.Zip.

My Structure is like this:

They are like:

my.jar!/Main.class
my.jar!/Aux.class
my.jar!/Other.class
my.jar!/images/image01.png
my.jar!/images/image02a.png
my.jar!/images/imwge034.png
my.jar!/images/imagAe01q.png
my.jar!/META-INF/manifest 

Right now I'm able to load for instance "images/image01.png" using:

    ImageIO.read(this.getClass().getResource("images/image01.png));

But only because I know the file name, for the rest I have to load them dynamically.

030
  • 10,842
  • 12
  • 78
  • 123
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • Just a thought - why not zip/jar images into a separate file and read the entries in it from your class in another jar? – Vineet Reynolds Sep 15 '09 at 19:50
  • 4
    Because it would need an "extra" step for distribution/installation. :( You know, end users. – OscarRyz Sep 15 '09 at 19:58
  • Well, I might be mistaken but jars can be embedded inside other jars. The one-jar(TM) packaging solution http://www.ibm.com/developerworks/java/library/j-onejar/ works on this basis. Except, in your case you do not require the ability load classes. – Vineet Reynolds Sep 15 '09 at 20:11
  • Given that you have created the jar, you might as well include the list of files within it rather than attempting any tricks. – Tom Hawtin - tackline Sep 15 '09 at 20:04

17 Answers17

111
CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
if (src != null) {
  URL jar = src.getLocation();
  ZipInputStream zip = new ZipInputStream(jar.openStream());
  while(true) {
    ZipEntry e = zip.getNextEntry();
    if (e == null)
      break;
    String name = e.getName();
    if (name.startsWith("path/to/your/dir/")) {
      /* Do something with this entry. */
      ...
    }
  }
} 
else {
  /* Fail... */
}

Note that in Java 7, you can create a FileSystem from the JAR (zip) file, and then use NIO's directory walking and filtering mechanisms to search through it. This would make it easier to write code that handles JARs and "exploded" directories.

David V
  • 11,531
  • 5
  • 42
  • 66
erickson
  • 265,237
  • 58
  • 395
  • 493
  • hey thanks... been looking for a way to do this for a few hours now !! – Newtopian May 19 '10 at 09:17
  • 13
    Yes this code works if we want to list all entries inside this jar file. But if I just want to list a subdirectory inside the jar, for example, **example.jar/dir1/dir2/**, how can I directly list all files inside this subdirectory? Or I need to unzip this jar file? I highly appreciate your help ! – Ensom Hodder Aug 11 '12 at 22:39
  • Mentioned Java 7 approach is listed in [@acheron55's answer](http://stackoverflow.com/a/28057735/603516). – Vadzim Aug 03 '15 at 10:13
  • 1
    @Vadzim Are you sure acheron55's answer is for Java 7? I didn't found Files.walk() or java.util.Stream in Java 7, but in Java 8: https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html – Bruce Sun Jan 22 '16 at 06:17
  • @BruceSun, in java 7 you can use [Files.walkFileTree(...)](http://stackoverflow.com/a/3154643/603516) instead. – Vadzim Jan 22 '16 at 10:13
  • This only works for extracting class, useless on resources which is most situation – tribbloid Sep 03 '16 at 22:43
  • @tribbloid That's not correct. This can be used to extract any file in the archive. What gave you your incorrect understanding? – erickson Sep 05 '16 at 00:15
  • Best solution. Solved the problem. – AnirbanDebnath Dec 14 '19 at 16:29
101

Code that works for both IDE's and .jar files:

import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;

public class ResourceWalker {
    public static void main(String[] args) throws URISyntaxException, IOException {
        URI uri = ResourceWalker.class.getResource("/resources").toURI();
        Path myPath;
        if (uri.getScheme().equals("jar")) {
            FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
            myPath = fileSystem.getPath("/resources");
        } else {
            myPath = Paths.get(uri);
        }
        Stream<Path> walk = Files.walk(myPath, 1);
        for (Iterator<Path> it = walk.iterator(); it.hasNext();){
            System.out.println(it.next());
        }
    }
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
acheron55
  • 5,429
  • 2
  • 23
  • 22
  • 7
    Fantastic!!! but URI uri = MyClass.class.getResource("/resources").toURI(); should have MyClass.class.getClassLoader().getResource("/resources").toURI(); i.e, getClassLoader(). Otherwise it was not working for me. – EMM Aug 15 '15 at 15:36
  • 15
    Don't forget to close `fileSystem`! – gmjonker Mar 10 '16 at 12:03
  • 4
    This should be the first answer for 1.8 (`walk` method in `Files` is only available in 1.8). The only problem is that the resources directory appears in the `Files.walk(myPath, 1)`, not just the files. I guess the first element can be simply ignored – toto_tico Apr 19 '16 at 23:12
  • @toto_tico I think it is 1.7, I wasn't using 1.8 when writing that answer – acheron55 Apr 21 '16 at 12:38
  • 1
    This is a good idea, but it is also unsafe. I've added an [answer](http://stackoverflow.com/questions/1429172/how-do-i-list-the-files-inside-a-jar-file/37730853#37730853) with a safer approach. – Eyal Roth Jun 09 '16 at 16:01
  • 1
    I have added a Java 7 implementation of this code and tested it on application servers too. See http://stackoverflow.com/questions/1429172/how-do-i-list-the-files-inside-a-jar-file/39974405#39974405 – Pino Oct 11 '16 at 09:40
  • What is the use case for the else path for `uri.getScheme().equals("jar")`? – Stefan Bormann Oct 13 '16 at 10:11
  • 1
    @StefanBormann for if you run it regular (in your IDE) and not in a built jar – phip1611 Mar 31 '18 at 20:23
  • 7
    `myPath = fileSystem.getPath("/resources");` doesn't work for me; it doesn't find anything. It should be "images" in my case and the "images"-directory is definitely included in my jar! – phip1611 Mar 31 '18 at 20:24
24

erickson's answer worked perfectly:

Here's the working code.

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
List<String> list = new ArrayList<String>();

if( src != null ) {
    URL jar = src.getLocation();
    ZipInputStream zip = new ZipInputStream( jar.openStream());
    ZipEntry ze = null;

    while( ( ze = zip.getNextEntry() ) != null ) {
        String entryName = ze.getName();
        if( entryName.startsWith("images") &&  entryName.endsWith(".png") ) {
            list.add( entryName  );
        }
    }

 }
 webimages = list.toArray( new String[ list.size() ] );

And I have just modify my load method from this:

File[] webimages = ... 
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex].getName() ));

To this:

String  [] webimages = ...

BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));
Community
  • 1
  • 1
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
14

I would like to expand on acheron55's answer, since it is a very non-safe solution, for several reasons:

  1. It doesn't close the FileSystem object.
  2. It doesn't check if the FileSystem object already exists.
  3. It isn't thread-safe.

This is somewhat a safer solution:

private static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>();

public void walk(String path) throws Exception {

    URI uri = getClass().getResource(path).toURI();
    if ("jar".equals(uri.getScheme())) {
        safeWalkJar(path, uri);
    } else {
        Files.walk(Paths.get(path));
    }
}

private void safeWalkJar(String path, URI uri) throws Exception {

    synchronized (getLock(uri)) {    
        // this'll close the FileSystem object at the end
        try (FileSystem fs = getFileSystem(uri)) {
            Files.walk(fs.getPath(path));
        }
    }
}

private Object getLock(URI uri) {

    String fileName = parseFileName(uri);  
    locks.computeIfAbsent(fileName, s -> new Object());
    return locks.get(fileName);
}

private String parseFileName(URI uri) {
    
    String schemeSpecificPart = uri.getSchemeSpecificPart();
    return schemeSpecificPart.substring(0, schemeSpecificPart.indexOf("!"));
}

private FileSystem getFileSystem(URI uri) throws IOException {

    try {
        return FileSystems.getFileSystem(uri);
    } catch (FileSystemNotFoundException e) {
        return FileSystems.newFileSystem(uri, Collections.<String, String>emptyMap());
    }
}   

There's no real need to synchronize over the file name; one could simply synchronize on the same object every time (or make the method synchronized), it's purely an optimization.

I would say that this is still a problematic solution, since there might be other parts in the code that use the FileSystem interface over the same files, and it could interfere with them (even in a single threaded application).
Also, it doesn't check for nulls (for instance, on getClass().getResource().

This particular Java NIO interface is kind of horrible, since it introduces a global/singleton non thread-safe resource, and its documentation is extremely vague (a lot of unknowns due to provider specific implementations). Results may vary for other FileSystem providers (not JAR). Maybe there's a good reason for it being that way; I don't know, I haven't researched the implementations.

Lebecca
  • 2,406
  • 15
  • 32
Eyal Roth
  • 3,895
  • 6
  • 34
  • 45
  • 2
    Synchronization of external resources, like FS, does not have much sense within one VM. There can be other applications accessing it outside your VM. Besides even inside your own application, your locking based on filenames can be easily bypassed. With this things it is better to rely on OS synchronization mechanisms, like file locking. – Espinosa Apr 05 '17 at 09:27
  • @Espinosa The file-name lock mechanism could totally be bypassed; my answer is not safe enough as well, but I believe it's the most you can get with Java NIO's with minimal effort. Relying on the OS to manage the locks, or not being in control of which applications access which files is a bad practice IMHO, unless you're building a costumer-based app -- say, a text editor. Not managing locks on your own will either cause exceptions to be thrown, or cause threads to block the application - both should be avoided. – Eyal Roth Apr 06 '17 at 11:02
9

So I guess my main problem would be, how to know the name of the jar where my main class lives.

Assuming that your project is packed in a Jar (not necessarily true!), you can use ClassLoader.getResource() or findResource() with the class name (followed by .class) to get the jar that contains a given class. You'll have to parse the jar name from the URL that gets returned (not that tough), which I will leave as an exercise for the reader :-)

Be sure to test for the case where the class is not part of a jar.

Kevin Day
  • 16,067
  • 8
  • 44
  • 68
  • 1
    huh - interesting that this would have been down moded without comment... We use the above technique all the time and it works just fine. – Kevin Day Nov 10 '09 at 19:58
  • An old issue, but to me this seems like a fine hack. Upvoted back to zero :) – Tuukka Mustonen Sep 15 '10 at 09:02
  • Upvoted, because this is the only solution listed here for the case when a class does not have a `CodeSource`. – jr. Nov 24 '13 at 03:40
  • Need to remember that a classloader's `getResources` will search for all classloader paths, so it'll include resources from other jars. So, better to place own resources into uniquely named folder hierarchy. – kolobok Aug 08 '22 at 10:54
9

I've ported acheron55's answer to Java 7 and closed the FileSystem object. This code works in IDE's, in jar files and in a jar inside a war on Tomcat 7; but note that it does not work in a jar inside a war on JBoss 7 (it gives FileSystemNotFoundException: Provider "vfs" not installed, see also this post). Furthermore, like the original code, it is not thread safe, as suggested by errr. For these reasons I have abandoned this solution; however, if you can accept these issues, here is my ready-made code:

import java.io.IOException;
import java.net.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;

public class ResourceWalker {

    public static void main(String[] args) throws URISyntaxException, IOException {
        URI uri = ResourceWalker.class.getResource("/resources").toURI();
        System.out.println("Starting from: " + uri);
        try (FileSystem fileSystem = (uri.getScheme().equals("jar") ? FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap()) : null)) {
            Path myPath = Paths.get(uri);
            Files.walkFileTree(myPath, new SimpleFileVisitor<Path>() { 
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    System.out.println(file);
                    return FileVisitResult.CONTINUE;
                }
            });
        }
    }
}
Community
  • 1
  • 1
Pino
  • 7,468
  • 6
  • 50
  • 69
  • This code doesn't work for me, I get `NullPointerException` on line with `ResourceWalker.class.getResource("/resources").toURI()`, and if I use "" as parameter, I get the following error: `Starting from: rsrc:com/betalord/sgx/util/` `java.nio.file.FileSystemNotFoundException: Provider "rsrc" not installed` – Betalord Jan 06 '21 at 21:35
6

Here is an example of using Reflections library to recursively scan classpath by regex name pattern augmented with a couple of Guava perks to to fetch resources contents:

Reflections reflections = new Reflections("com.example.package", new ResourcesScanner());
Set<String> paths = reflections.getResources(Pattern.compile(".*\\.template$"));

Map<String, String> templates = new LinkedHashMap<>();
for (String path : paths) {
    log.info("Found " + path);
    String templateName = Files.getNameWithoutExtension(path);
    URL resource = getClass().getClassLoader().getResource(path);
    String text = Resources.toString(resource, StandardCharsets.UTF_8);
    templates.put(templateName, text);
}

This works with both jars and exploded classes.

Vadzim
  • 24,954
  • 11
  • 143
  • 151
  • Beware that reflections still doesn't support Java 9 and above: https://github.com/ronmamo/reflections/issues/186. There are links to competing libraries there. – Vadzim Aug 14 '19 at 15:03
5

Here's a method I wrote for a "run all JUnits under a package". You should be able to adapt it to your needs.

private static void findClassesInJar(List<String> classFiles, String path) throws IOException {
    final String[] parts = path.split("\\Q.jar\\\\E");
    if (parts.length == 2) {
        String jarFilename = parts[0] + ".jar";
        String relativePath = parts[1].replace(File.separatorChar, '/');
        JarFile jarFile = new JarFile(jarFilename);
        final Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            final JarEntry entry = entries.nextElement();
            final String entryName = entry.getName();
            if (entryName.startsWith(relativePath)) {
                classFiles.add(entryName.replace('/', File.separatorChar));
            }
        }
    }
}

Edit: Ah, in that case, you might want this snippet as well (same use case :) )

private static File findClassesDir(Class<?> clazz) {
    try {
        String path = clazz.getProtectionDomain().getCodeSource().getLocation().getFile();
        final String codeSourcePath = URLDecoder.decode(path, "UTF-8");
        final String thisClassPath = new File(codeSourcePath, clazz.getPackage().getName().repalce('.', File.separatorChar));
    } catch (UnsupportedEncodingException e) {
        throw new AssertionError("impossible", e);
    }
}
Ran Biron
  • 6,317
  • 5
  • 37
  • 67
  • 1
    I guess the big problem is to know the jar file name in first place. It is the jar where the Main-Class: lives. – OscarRyz Sep 15 '09 at 19:41
5

Just to mention that if you are already using Spring, you can take advantage of the PathMatchingResourcePatternResolver.

For instance to get all the PNG files from a images folder in resources

ClassLoader cl = this.getClass().getClassLoader(); 
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
Resource[] resources = resolver.getResources("images/*.png");
for (Resource r: resources){
    logger.info(r.getFilename());
    // From your example
    // ImageIO.read(cl.getResource("images/" + r.getFilename()));
}
Kraiss
  • 919
  • 7
  • 22
3

A jar file is just a zip file with a structured manifest. You can open the jar file with the usual java zip tools and scan the file contents that way, inflate streams, etc. Then use that in a getResourceAsStream call, and it should be all hunky dory.

EDIT / after clarification

It took me a minute to remember all the bits and pieces and I'm sure there are cleaner ways to do it, but I wanted to see that I wasn't crazy. In my project image.jpg is a file in some part of the main jar file. I get the class loader of the main class (SomeClass is the entry point) and use it to discover the image.jpg resource. Then some stream magic to get it into this ImageInputStream thing and everything is fine.

InputStream inputStream = SomeClass.class.getClassLoader().getResourceAsStream("image.jpg");
JPEGImageReaderSpi imageReaderSpi = new JPEGImageReaderSpi();
ImageReader ir = imageReaderSpi.createReaderInstance();
ImageInputStream iis = new MemoryCacheImageInputStream(inputStream);
ir.setInput(iis);
....
ir.read(0); //will hand us a buffered image
Mikeb
  • 6,271
  • 3
  • 27
  • 41
  • This jar contains the main program and the resources. How do I refer to the self jar? from within the jar file? – OscarRyz Sep 15 '09 at 19:37
  • To refer to the JAR file, just use "blah.JAR" as the String. You can use `new File("blah.JAR")` to create a File object that represents the JAR, for example. Just replace "blah.JAR" with the name of your JAR. – Thomas Owens Sep 15 '09 at 19:39
  • If its the same jar that you are already running out of, the class loader should be able to see stuff inside the jar ... I misunderstood what you were trying to do initially. – Mikeb Sep 15 '09 at 20:11
  • 2
    Well yes, I already have that, the problem is when I need something like: "...getResourceAsStream("*.jpg"); ... " That is, dynamically, list the files contained. – OscarRyz Sep 15 '09 at 21:27
3

Given an actual JAR file, you can list the contents using JarFile.entries(). You will need to know the location of the JAR file though - you can't just ask the classloader to list everything it could get at.

You should be able to work out the location of the JAR file based on the URL returned from ThisClassName.class.getResource("ThisClassName.class"), but it may be a tiny bit fiddly.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Reading your answer another question raised. What would yield the call: this.getClass().getResource("/my_directory"); It should return an URL that could in turn be .... used as directory? Nahh... let me try it. – OscarRyz Sep 15 '09 at 19:40
  • You always know the location of the JAR - it's in ".". As long as the name of the JAR is known to be something, you can use a String constant somewhere. Now, if people go changing the name of the JAR... – Thomas Owens Sep 15 '09 at 19:41
  • @Thomas: That's assuming you're running the app from the current directory. What's wrong with "java -jar foo/bar/baz.jar"? – Jon Skeet Sep 15 '09 at 19:46
  • I believe (and would have to verify), that if you had code in your Jar that was `new File("baz.jar)`, the File object would represent your JAR file. – Thomas Owens Sep 15 '09 at 19:50
  • @Thomas: I don't believe so. I believe it's would be relative to the current working directory of the process. I'd have to check too though :) – Jon Skeet Sep 15 '09 at 20:20
  • One liner for this solution: `new JarFile(new File(ThisClassName.class.getProtectionDomain().getCodeSource().getLocation().toURI())).entries()` – Torsten Sep 11 '18 at 14:54
3

Some time ago I made a function that gets classess from inside JAR:

public static Class[] getClasses(String packageName) 
throws ClassNotFoundException{
    ArrayList<Class> classes = new ArrayList<Class> ();

    packageName = packageName.replaceAll("\\." , "/");
    File f = new File(jarName);
    if(f.exists()){
        try{
            JarInputStream jarFile = new JarInputStream(
                    new FileInputStream (jarName));
            JarEntry jarEntry;

            while(true) {
                jarEntry=jarFile.getNextJarEntry ();
                if(jarEntry == null){
                    break;
                }
                if((jarEntry.getName ().startsWith (packageName)) &&
                        (jarEntry.getName ().endsWith (".class")) ) {
                    classes.add(Class.forName(jarEntry.getName().
                            replaceAll("/", "\\.").
                            substring(0, jarEntry.getName().length() - 6)));
                }
            }
        }
        catch( Exception e){
            e.printStackTrace ();
        }
        Class[] classesA = new Class[classes.size()];
        classes.toArray(classesA);
        return classesA;
    }else
        return null;
}
berni
  • 1,955
  • 1
  • 19
  • 16
2
public static ArrayList<String> listItems(String path) throws Exception{
    InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(path);
    byte[] b = new byte[in.available()];
    in.read(b);
    String data = new String(b);
    String[] s = data.split("\n");
    List<String> a = Arrays.asList(s);
    ArrayList<String> m = new ArrayList<>(a);
    return m;
}
Samuel Philipp
  • 10,631
  • 12
  • 36
  • 56
Felix G.
  • 21
  • 1
  • 3
    While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please [include an explanation for your code](//meta.stackexchange.com/q/114762/269535), as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Samuel Philipp Aug 24 '19 at 19:43
  • data is empty when we execute the code from a jar file. – Aguid May 05 '20 at 22:37
1

There are two very useful utilities both called JarScan:

  1. www.inetfeedback.com/jarscan

  2. jarscan.dev.java.net

See also this question: JarScan, scan all JAR files in all subfolders for specific class

Community
  • 1
  • 1
jgran
  • 1,111
  • 2
  • 11
  • 21
1

The most robust mechanism for listing all resources in the classpath is currently to use this pattern with ClassGraph, because it handles the widest possible array of classpath specification mechanisms, including the new JPMS module system. (I am the author of ClassGraph.)

How to know the name of the JAR file where my main class lives?

URI mainClasspathElementURI;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
        .enableClassInfo().scan()) {
    mainClasspathElementURI =
            scanResult.getClassInfo("x.y.z.MainClass").getClasspathElementURI();
}

How can I read the contents of a directory in a similar fashion within a JAR file?

List<String> classpathElementResourcePaths;
try (ScanResult scanResult = new ClassGraph().overrideClasspath(mainClasspathElementURI)
        .scan()) {
    classpathElementResourcePaths = scanResult.getAllResources().getPaths();
}

There are lots of other ways to deal with resources too.

Luke Hutchison
  • 8,186
  • 2
  • 45
  • 40
1

One more for the road that's a bit more flexible for matching specific filenames because it uses wildcard globbing. In a functional style this could resemble:

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;

import static java.nio.file.FileSystems.getDefault;
import static java.nio.file.FileSystems.newFileSystem;
import static java.util.Collections.emptyMap;

/**
 * Responsible for finding file resources.
 */
public class ResourceWalker {
  /**
   * Globbing pattern to match font names.
   */
  public static final String GLOB_FONTS = "**.{ttf,otf}";

  /**
   * @param directory The root directory to scan for files matching the glob.
   * @param c         The consumer function to call for each matching path
   *                  found.
   * @throws URISyntaxException Could not convert the resource to a URI.
   * @throws IOException        Could not walk the tree.
   */
  public static void walk(
    final String directory, final String glob, final Consumer<Path> c )
    throws URISyntaxException, IOException {
    final var resource = ResourceWalker.class.getResource( directory );
    final var matcher = getDefault().getPathMatcher( "glob:" + glob );

    if( resource != null ) {
      final var uri = resource.toURI();
      final Path path;
      FileSystem fs = null;

      if( "jar".equals( uri.getScheme() ) ) {
        fs = newFileSystem( uri, emptyMap() );
        path = fs.getPath( directory );
      }
      else {
        path = Paths.get( uri );
      }

      try( final var walk = Files.walk( path, 10 ) ) {
        for( final var it = walk.iterator(); it.hasNext(); ) {
          final Path p = it.next();
          if( matcher.matches( p ) ) {
            c.accept( p );
          }
        }
      } finally {
        if( fs != null ) { fs.close(); }
      }
    }
  }
}

Consider parameterizing the file extensions, left an exercise for the reader.

Be careful with Files.walk. According to the documentation:

This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directories are closed promptly after the stream's operations have completed.

Likewise, newFileSystem must be closed, but not before the walker has had a chance to visit the file system paths.

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
0

Just a different way of listing/reading files from a jar URL and it does it recursively for nested jars

https://gist.github.com/trung/2cd90faab7f75b3bcbaa

URL urlResource = Thead.currentThread().getContextClassLoader().getResource("foo");
JarReader.read(urlResource, new InputStreamCallback() {
    @Override
    public void onFile(String name, InputStream is) throws IOException {
        // got file name and content stream 
    }
});
trung
  • 1,104
  • 11
  • 13