Jak wyświetlić listę plików w pliku JAR?

114

Mam ten kod, który czyta wszystkie pliki z katalogu.

    File textFolder = new File("text_directory");

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

Działa świetnie. Wypełnia tablicę wszystkimi plikami kończącymi się na „.txt” z katalogu „text_directory”.

Jak można odczytać zawartość katalogu, w podobny sposób w pliku JAR?

To, co naprawdę chcę zrobić, to wymienić wszystkie obrazy w moim pliku JAR, aby móc je załadować za pomocą:

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

(To działa, ponieważ „CompanyLogo” jest „zakodowane na stałe”, ale liczba obrazów w pliku JAR może wynosić od 10 do 200 zmiennej długości).

EDYTOWAĆ

Więc myślę, że moim głównym problemem byłoby: Jak poznać nazwę pliku JAR, w którym mieszka moja główna klasa?

Przyznane, że mogłem to przeczytać za pomocą java.util.Zip.

Moja struktura wygląda tak:

Oni są jak:

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 

W tej chwili mogę załadować na przykład plik „images / image01.png” za pomocą:

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

Ale tylko dlatego, że znam nazwy plików, do reszty muszę je ładować dynamicznie.

OscarRyz
źródło
Tylko myśl - dlaczego by nie spakować / jar do osobnego pliku i przeczytać zawarte w nim wpisy z klasy w innym jar?
Vineet Reynolds
3
Ponieważ do dystrybucji / instalacji potrzebny byłby „dodatkowy” krok. :( Wiesz, użytkownicy końcowi.
OscarRyz,
Biorąc pod uwagę, że utworzyłeś jar, równie dobrze możesz umieścić w nim listę plików, zamiast próbować wykonywać jakiekolwiek sztuczki.
Tom Hawtin - tackline
Cóż, mogę się mylić, ale słoiki można osadzić w innych słoikach. Na tej podstawie działa opakowanie typu one-jar (TM) ibm.com/developerworks/java/library/j-onejar . Z wyjątkiem tego, że w twoim przypadku nie potrzebujesz klas obciążenia zdolności.
Vineet Reynolds

Odpowiedzi:

91
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... */
}

Zwróć uwagę, że w Javie 7 możesz utworzyć plik FileSystemJAR (zip), a następnie przeszukać go za pomocą mechanizmów przechodzenia po katalogach i filtrowania NIO. Ułatwiłoby to pisanie kodu obsługującego pliki JAR i „eksplodowane” katalogi.

erickson
źródło
hej, dzięki ... od kilku godzin szukam sposobu, żeby to zrobić !!
Newtopian
9
Tak, ten kod działa, jeśli chcemy wyświetlić wszystkie wpisy w tym pliku jar. Ale jeśli chcę tylko wyświetlić podkatalog w katalogu jar, na przykład example.jar / dir1 / dir2 / , jak mogę bezpośrednio wyświetlić listę wszystkich plików w tym podkatalogu? Czy muszę rozpakować ten plik jar? Bardzo doceniam twoją pomoc!
Ensom Hodder,
Wspomniane podejście do Java 7 jest wymienione w odpowiedzi @ acheron55 .
Vadzim
@Vadzim Czy na pewno odpowiedź acheron55 dotyczy Java 7? Nie znalazłem Files.walk () ani java.util.Stream w Javie 7, ale w Javie 8: docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html
Bruce Niedziela
@BruceSun, w java 7 możesz zamiast tego użyć Files.walkFileTree (...) .
Vadzim
80

Kod, który działa zarówno dla plików IDE, jak i .jar:

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());
        }
    }
}
acheron55
źródło
5
FileSystems.newFileSystem()zajmuje a Map<String, ?>, więc musisz określić Collections.emptyMap(), że ma zwrócić odpowiednio wpisany. Działa to: Collections.<String, Object>emptyMap().
Zero3
6
Fantastyczny!!! ale URI uri = MyClass.class.getResource ("/ resources"). toURI (); powinien mieć MyClass.class.getClassLoader (). getResource ("/ resources"). toURI (); tj. getClassLoader (). W przeciwnym razie to nie działa dla mnie.
EMM
8
Nie zapomnij zamknąć fileSystem!
gmjonker
3
To powinna być pierwsza odpowiedź dla 1.8 ( walkmetoda w Filesjest dostępna tylko w 1.8). Jedynym problemem jest to, że katalog zasobów pojawia się w Files.walk(myPath, 1)pliku, a nie tylko w plikach. Myślę, że pierwszy element można po prostu zignorować
toto_tico 19.04.2016
4
myPath = fileSystem.getPath("/resources");nie działa dla mnie; nic nie znajduje. W moim przypadku powinno to być "obrazy", a katalog "obrazy" jest zdecydowanie zawarty w moim słoiku!
phip1611
21

odpowiedź Ericksona zadziałała idealnie:

Oto działający kod.

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

I właśnie zmodyfikowałem moją metodę ładowania z tego:

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

Do tego:

String  [] webimages = ...

BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));
OscarRyz
źródło
9

Chciałbym rozwinąć odpowiedź acheron55 , ponieważ jest to bardzo niebezpieczne rozwiązanie z kilku powodów:

  1. Nie zamyka FileSystemobiektu.
  2. Nie sprawdza, czy FileSystemobiekt już istnieje.
  3. To nie jest bezpieczne dla wątków.

To jest nieco bezpieczniejsze rozwiązanie:

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

Nie ma rzeczywistej potrzeby synchronizacji nazwy pliku; można po prostu zsynchronizować ten sam obiekt za każdym razem (lub stworzyć metodę synchronized), to czysta optymalizacja.

Powiedziałbym, że jest to nadal problematyczne rozwiązanie, ponieważ w kodzie mogą znajdować się inne części, które używają FileSysteminterfejsu na tych samych plikach, co może kolidować z nimi (nawet w aplikacji jednowątkowej).
Nie sprawdza też nulls (na przykład on getClass().getResource().

Ten konkretny interfejs Java NIO jest trochę okropny, ponieważ wprowadza globalny / pojedynczy zasób, który nie jest bezpieczny dla wątków, a jego dokumentacja jest bardzo niejasna (wiele niewiadomych ze względu na implementacje specyficzne dla dostawcy). Wyniki mogą się różnić w przypadku innych FileSystemdostawców (nie JAR). Może jest dobry powód, dla którego tak jest; Nie wiem, nie badałem wdrożeń.

Eyal Roth
źródło
1
Synchronizacja zasobów zewnętrznych, takich jak FS, nie ma większego sensu w ramach jednej maszyny wirtualnej. Mogą istnieć inne aplikacje uzyskujące do niego dostęp poza maszyną wirtualną. Poza tym nawet wewnątrz własnej aplikacji można łatwo ominąć blokowanie oparte na nazwach plików. Mając to na uwadze, lepiej jest polegać na mechanizmach synchronizacji systemu operacyjnego, takich jak blokowanie plików.
Espinosa,
@Espinosa Mechanizm blokady nazwy pliku można było całkowicie ominąć; moja odpowiedź również nie jest wystarczająco bezpieczna, ale uważam, że jest to najwięcej, co można uzyskać dzięki Java NIO przy minimalnym wysiłku. Poleganie na systemie operacyjnym w celu zarządzania blokadami lub brak kontroli nad tym, które aplikacje mają dostęp do których plików, jest złą praktyką IMHO, chyba że tworzysz aplikację opartą na klientach - powiedzmy, edytor tekstu. Brak zarządzania blokadami na własną rękę spowoduje albo wyrzucenie wyjątków, albo spowoduje, że wątki będą blokować aplikację - należy unikać obu.
Eyal Roth
8

Więc myślę, że moim głównym problemem byłoby to, jak poznać nazwę słoika, w którym mieszka moja główna klasa.

Zakładając, że Twój projekt jest zapakowany w Jar (niekoniecznie prawda!), Możesz użyć ClassLoader.getResource () lub findResource () z nazwą klasy (po której następuje .class), aby uzyskać jar zawierający daną klasę. Będziesz musiał przeanalizować nazwę słoika z adresu URL, który zostanie zwrócony (nie taki trudny), co zostawię jako ćwiczenie dla czytelnika :-)

Pamiętaj, aby przetestować przypadek, w którym klasa nie jest częścią słoika.

Kevin Day
źródło
1
huh - ciekawe, że zostałoby to zmodyfikowane bez komentarza ... Cały czas używamy powyższej techniki i działa dobrze.
Dzień Kevina
Stary problem, ale wydaje mi się, że to niezły hack. Głos za zero :)
Tuukka Mustonen
Głosowano za, ponieważ jest to jedyne wymienione tutaj rozwiązanie w przypadku, gdy klasa nie ma rozszerzenia CodeSource.
Przywróć Monikę 2331977
7

Mam przeniesiony odpowiedź acheron55 w Java 7 i zamknął FileSystemobiekt. Ten kod działa w IDE, w plikach jar i w jar w trakcie wojny z Tomcat 7; ale zwróć uwagę, że nie działa w słoiku wewnątrz wojny na JBoss 7 (daje FileSystemNotFoundException: Provider "vfs" not installed, zobacz także ten post ). Ponadto, podobnie jak oryginalny kod, nie jest bezpieczny dla wątków, jak sugeruje errr . Z tych powodów porzuciłem to rozwiązanie; jeśli jednak możesz zaakceptować te kwestie, oto mój gotowy kod:

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;
                }
            });
        }
    }
}
Pino
źródło
5

Oto metoda, którą napisałem dla „uruchomienia wszystkich JUnits w pakiecie”. Powinieneś być w stanie dostosować go do swoich potrzeb.

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

Edycja: Ach, w takim przypadku możesz również chcieć tego fragmentu (ten sam przypadek użycia :))

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
źródło
1
Myślę, że dużym problemem jest przede wszystkim znajomość nazwy pliku jar. To słoik, w którym mieszka klasa główna.
OscarRyz,
5

Oto przykład użycia biblioteki Reflections do rekurencyjnego skanowania ścieżki klas według wzorca nazwy wyrażenia regularnego, wzbogaconego o kilka profitów Guava, aby pobrać zawartość zasobów:

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

Działa to zarówno w przypadku słoików, jak i klas eksplodowanych.

Vadzim
źródło
Uważaj, że odbicia nadal nie obsługują Java 9 i nowszych wersji: github.com/ronmamo/reflections/issues/186 . Znajdują się tam linki do konkurencyjnych bibliotek.
Vadzim
3

Plik jar to po prostu plik ZIP z manifestem strukturalnym. Możesz otworzyć plik jar za pomocą zwykłych narzędzi zip Java i przeskanować w ten sposób zawartość pliku, nadmuchać strumienie itp. Następnie użyj tego w wywołaniu getResourceAsStream, a wszystko powinno być hunky dory.

EDYCJA / po wyjaśnieniu

Zapamiętanie wszystkich drobiazgów zajęło mi minutę i jestem pewien, że można to zrobić czystszym sposobem, ale chciałem zobaczyć, że nie jestem szalony. W moim projekcie image.jpg to plik w jakiejś części głównego pliku jar. Otrzymuję program ładujący klasy głównej (punktem wejścia jest SomeClass) i używam go do odkrywania zasobu image.jpg. Następnie trochę magii strumienia, aby przenieść to do tego elementu ImageInputStream i wszystko jest w porządku.

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
źródło
Ten słoik zawiera główny program i zasoby. Jak mam się odnosić do słoika? z poziomu pliku jar?
OscarRyz,
Aby odnieść się do pliku JAR, użyj po prostu „blah.JAR” jako ciągu znaków. Można użyć na przykład new File("blah.JAR")do utworzenia obiektu File, który reprezentuje plik JAR. Po prostu zastąp „blah.JAR” nazwą swojego pliku JAR.
Thomas Owens,
Jeśli jest to ten sam słoik, z którego już się wyczerpujesz, program ładujący klasy powinien być w stanie zobaczyć zawartość słoika ... Źle zrozumiałem, co próbujesz zrobić na początku.
Mikeb,
2
Cóż, tak, już to mam, problem jest, gdy potrzebuję czegoś takiego: "... getResourceAsStream (" *. Jpg "); ..." To znaczy dynamicznie wyświetla listę zawartych plików.
OscarRyz
3

Biorąc pod uwagę rzeczywisty plik JAR, możesz wyświetlić zawartość za pomocą JarFile.entries(). Musisz jednak znać lokalizację pliku JAR - nie możesz po prostu poprosić modułu ładującego klasy o listę wszystkiego, do czego może się dostać.

Powinno być możliwe ustalenie lokalizacji pliku JAR na podstawie adresu URL zwróconego z ThisClassName.class.getResource("ThisClassName.class"), ale może to być trochę kłopotliwe.

Jon Skeet
źródło
Czytając twoją odpowiedź, kolejne zadane pytanie. Co spowodowałoby wywołanie: this.getClass (). GetResource ("/ my_directory"); Powinien zwrócić adres URL, który z kolei mógłby być… użyty jako katalog? Nie ... pozwól mi spróbować.
OscarRyz,
Zawsze wiesz, gdzie znajduje się JAR - znajduje się w „.”. Dopóki wiadomo, że nazwa pliku JAR jest czymś, możesz gdzieś użyć stałej typu String. Teraz, jeśli ludzie zmienią nazwę JAR ...
Thomas Owens,
@Thomas: To przy założeniu, że uruchamiasz aplikację z bieżącego katalogu. Co jest nie tak z "java -jar foo / bar / baz.jar"?
Jon Skeet
Uważam (i musiałbym to zweryfikować), że gdybyś miał kod w swoim Jar new File("baz.jar), obiekt File reprezentowałby twój plik JAR.
Thomas Owens,
@Thomas: Nie sądzę. Uważam, że byłoby to względne w stosunku do bieżącego katalogu roboczego procesu. Też musiałbym sprawdzić :)
Jon Skeet,
3

Jakiś czas temu stworzyłem funkcję, która pobiera klasy z wnętrza JARa:

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
źródło
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;
}
Felix G.
źródło
3
Chociaż ten fragment kodu może rozwiązać problem, nie wyjaśnia, dlaczego ani jak odpowiada na pytanie. Dołącz wyjaśnienie swojego kodu , ponieważ naprawdę pomaga to poprawić jakość Twojego postu. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a osoby te mogą nie znać powodów, dla których zaproponowałeś kod.
Samuel Philipp
dane są puste, gdy wykonujemy kod z pliku jar.
Aguid
1

Najbardziej niezawodnym mechanizmem wyświetlania wszystkich zasobów w ścieżce klas jest obecnie używanie tego wzorca z ClassGraph , ponieważ obsługuje on najszerszy możliwy zestaw mechanizmów specyfikacji ścieżki klas , w tym nowy system modułów JPMS. (Jestem autorem ClassGraph.)

Jak poznać nazwę pliku JAR, w którym mieszka moja główna klasa?

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

Jak w podobny sposób odczytać zawartość katalogu w pliku JAR?

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

Istnieje wiele innych sposobów radzenia sobie z zasobami .

Luke Hutchison
źródło
1
Bardzo ładne opakowanie, łatwe do wykorzystania w moim projekcie Scala, dziękuję.
zslim
0

Po prostu inny sposób wyświetlania / odczytywania plików z adresu URL jar i robi to rekurencyjnie dla zagnieżdżonych plików jar

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
źródło
0

Jeszcze jeden na drogę:

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;

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

public class ResourceWalker {
  private static final PathMatcher FILE_MATCHER =
      FileSystems.getDefault().getPathMatcher( "glob:**.ttf" );

  public static List<Path> walk( final String directory )
      throws URISyntaxException, IOException {
    final List<Path> filenames = new ArrayList<>();
    final var resource = ResourceWalker.class.getResource( directory );

    if( resource != null ) {
      final var uri = resource.toURI();
      final var path = uri.getScheme().equals( "jar" )
          ? newFileSystem( uri, emptyMap() ).getPath( directory )
          : Paths.get( uri );
      final var walk = Files.walk( path, 10 );

      for( final var it = walk.iterator(); it.hasNext(); ) {
        final Path p = it.next();
        if( FILE_MATCHER.matches( p ) ) {
          filenames.add( p );
        }
      }
    }

    return filenames;
  }
}

Jest to nieco bardziej elastyczne w dopasowywaniu określonych nazw plików, ponieważ wykorzystuje globbing z użyciem symboli wieloznacznych.


Bardziej funkcjonalny styl:

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.util.function.Consumer;

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

/**
 * Responsible for finding file resources.
 */
public class ResourceWalker {
  private static final PathMatcher FILE_MATCHER =
      FileSystems.getDefault().getPathMatcher( "glob:**.ttf" );

  public static void walk( final String dirName, final Consumer<Path> f )
      throws URISyntaxException, IOException {
    final var resource = ResourceWalker.class.getResource( dirName );

    if( resource != null ) {
      final var uri = resource.toURI();
      final var path = uri.getScheme().equals( "jar" )
          ? newFileSystem( uri, emptyMap() ).getPath( dirName )
          : Paths.get( uri );
      final var walk = Files.walk( path, 10 );

      for( final var it = walk.iterator(); it.hasNext(); ) {
        final Path p = it.next();
        if( FILE_MATCHER.matches( p ) ) {
          f.accept( p );
        }
      }
    }
  }
}
Dave Jarvis
źródło