Wie liste ich die Dateien in einer JAR-Datei auf?

114

Ich habe diesen Code, der alle Dateien aus einem Verzeichnis liest.

    File textFolder = new File("text_directory");

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

Es funktioniert großartig. Es füllt das Array mit allen Dateien, die mit ".txt" aus dem Verzeichnis "text_directory" enden.

Wie kann ich den Inhalt eines Verzeichnisses in einer ähnlichen Art und Weise lesen in einer JAR - Datei?

Was ich also wirklich tun möchte, ist, alle Bilder in meiner JAR-Datei aufzulisten, damit ich sie laden kann mit:

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

(Das funktioniert, weil das "CompanyLogo" "fest codiert" ist, aber die Anzahl der Bilder in der JAR-Datei zwischen 10 und 200 variabler Länge liegen kann.)

BEARBEITEN

Ich denke, mein Hauptproblem wäre: Woher weiß ich den Namen der JAR-Datei, in der meine Hauptklasse lebt?

Zugegeben, ich konnte es mit lesen java.util.Zip.

Meine Struktur ist wie folgt:

Sie sind wie:

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 

Im Moment kann ich zum Beispiel "images / image01.png" laden mit:

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

Aber nur weil ich den Dateinamen kenne, muss ich sie im Übrigen dynamisch laden.

OscarRyz
quelle
Nur ein Gedanke - warum nicht Bilder in eine separate Datei packen und die darin enthaltenen Einträge aus Ihrer Klasse in einem anderen Glas lesen?
Vineet Reynolds
3
Weil es einen "zusätzlichen" Schritt für die Verteilung / Installation benötigen würde. :( Sie wissen, Endbenutzer.
OscarRyz
Wenn Sie das JAR erstellt haben, können Sie auch die Liste der Dateien darin einfügen, anstatt Tricks zu versuchen.
Tom Hawtin - Tackline
Nun, ich könnte mich irren, aber Gläser können in andere Gläser eingebettet werden. Die Ein-Glas-Verpackungslösung (TM) ibm.com/developerworks/java/library/j-onejar funktioniert auf dieser Basis. In Ihrem Fall benötigen Sie jedoch keine Fähigkeitsladeklassen.
Vineet Reynolds

Antworten:

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

Beachten Sie, dass Sie in Java 7 eine FileSystemDatei aus der JAR-Datei (zip) erstellen und diese dann mithilfe der NIO-Mechanismen zum Durchsuchen und Filtern von Verzeichnissen durchsuchen können. Dies würde es einfacher machen, Code zu schreiben, der JARs und "explodierte" Verzeichnisse verarbeitet.

erickson
quelle
hey danke ... habe seit ein paar Stunden nach einer Möglichkeit gesucht, dies zu tun !!
Newtopian
9
Ja, dieser Code funktioniert, wenn wir alle Einträge in dieser JAR-Datei auflisten möchten. Aber wenn ich nur ein Unterverzeichnis in der JAR-Liste auflisten möchte , z. B. example.jar / dir1 / dir2 / , wie kann ich dann alle Dateien in diesem Unterverzeichnis direkt auflisten ? Oder muss ich diese JAR-Datei entpacken? Ich schätze Ihre Hilfe sehr!
Ensom Hodder
Der erwähnte Java 7-Ansatz ist in der Antwort von @ acheron55 aufgeführt .
Vadzim
@Vadzim Sind Sie sicher, dass die Antwort von acheron55 für Java 7 ist? Ich habe Files.walk () oder java.util.Stream nicht in Java 7 gefunden, sondern in Java 8: docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html
Bruce So
@BruceSun, in Java 7 können Sie stattdessen Files.walkFileTree (...) verwenden.
Vadzim
79

Code, der sowohl für IDE- als auch für JAR-Dateien funktioniert:

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
quelle
5
FileSystems.newFileSystem()nimmt ein Map<String, ?>, also müssen Sie angeben, Collections.emptyMap()dass es ein entsprechend typisiertes zurückgeben muss. Das funktioniert : Collections.<String, Object>emptyMap().
Zero3
6
Fantastisch!!! aber URI uri = MyClass.class.getResource ("/ resources"). toURI (); sollte MyClass.class.getClassLoader () haben. getResource ("/ resources"). toURI (); dh getClassLoader (). Sonst hat es bei mir nicht funktioniert.
EMM
8
Vergiss nicht zu schließen fileSystem!
gmjonker
3
Dies sollte die erste Antwort für 1.8 sein ( walkMethode in Filesist nur in 1.8 verfügbar). Das einzige Problem ist, dass das Ressourcenverzeichnis Files.walk(myPath, 1)nicht nur in den Dateien angezeigt wird . Ich denke, das erste Element kann einfach ignoriert werden
toto_tico
4
myPath = fileSystem.getPath("/resources");funktioniert bei mir nicht; es findet nichts. Es sollte in meinem Fall "Bilder" sein und das "Bilder" -Verzeichnis ist definitiv in meinem Glas enthalten!
Phip1611
21

ericksons antwort funktionierte perfekt:

Hier ist der Arbeitscode.

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

Und ich habe gerade meine Lademethode daraus geändert:

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

Dazu:

String  [] webimages = ...

BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));
OscarRyz
quelle
9

Ich möchte die Antwort von acheron55 erweitern , da es sich aus mehreren Gründen um eine sehr unsichere Lösung handelt:

  1. Das FileSystemObjekt wird nicht geschlossen .
  2. Es wird nicht überprüft, ob das FileSystemObjekt bereits vorhanden ist.
  3. Es ist nicht threadsicher.

Dies ist eine etwas sicherere Lösung:

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

Es besteht keine wirkliche Notwendigkeit, über den Dateinamen zu synchronisieren. Man könnte einfach jedes Mal mit demselben Objekt synchronisieren (oder die Methode erstellen synchronized), es ist lediglich eine Optimierung.

Ich würde sagen, dass dies immer noch eine problematische Lösung ist, da es möglicherweise andere Teile im Code gibt, die die FileSystemSchnittstelle über dieselben Dateien verwenden, und sie möglicherweise stören (sogar in einer einzelnen Thread-Anwendung).
Außerdem wird nicht nach nulls gesucht (z. B. nach)getClass().getResource() .

Diese spezielle Java NIO-Schnittstelle ist schrecklich, da sie eine nicht threadsichere globale / Singleton-Ressource einführt und ihre Dokumentation äußerst vage ist (viele Unbekannte aufgrund anbieterspezifischer Implementierungen). Die Ergebnisse können für andere FileSystemAnbieter (nicht JAR) variieren . Vielleicht gibt es einen guten Grund dafür; Ich weiß nicht, ich habe die Implementierungen nicht recherchiert.

Eyal Roth
quelle
1
Die Synchronisation externer Ressourcen wie FS hat innerhalb einer VM wenig Sinn. Es können andere Anwendungen außerhalb Ihrer VM darauf zugreifen. Außerdem kann Ihre Sperre basierend auf Dateinamen auch innerhalb Ihrer eigenen Anwendung leicht umgangen werden. Bei diesen Dingen ist es besser, sich auf Betriebssystemsynchronisationsmechanismen wie das Sperren von Dateien zu verlassen.
Espinosa
@Espinosa Der Mechanismus zum Sperren von Dateinamen konnte vollständig umgangen werden. Meine Antwort ist auch nicht sicher genug, aber ich glaube, es ist das Beste, was Sie mit Java NIOs mit minimalem Aufwand erreichen können. Es ist meiner Meinung nach eine schlechte Praxis, sich auf das Betriebssystem zu verlassen, um die Sperren zu verwalten, oder nicht zu kontrollieren, welche Anwendungen auf welche Dateien zugreifen, es sei denn, Sie erstellen eine kundenbasierte App - beispielsweise einen Texteditor. Wenn Sie Sperren nicht selbst verwalten, werden entweder Ausnahmen ausgelöst oder Threads blockieren die Anwendung. Beides sollte vermieden werden.
Eyal Roth
8

Ich denke, mein Hauptproblem wäre, wie man den Namen des Glases kennt, in dem meine Hauptklasse lebt.

Angenommen, Ihr Projekt ist in einem Jar gepackt (nicht unbedingt wahr!), Können Sie ClassLoader.getResource () oder findResource () mit dem Klassennamen (gefolgt von .class) verwenden, um das Jar zu erhalten, das eine bestimmte Klasse enthält. Sie müssen den JAR-Namen von der URL analysieren, die zurückgegeben wird (nicht so schwierig), was ich als Übung für den Leser hinterlassen werde :-)

Stellen Sie sicher, dass Sie den Fall testen, in dem die Klasse nicht Teil eines Glases ist.

Kevin Day
quelle
1
huh - interessant, dass dies kommentarlos heruntermodifiziert worden wäre ... Wir verwenden die obige Technik die ganze Zeit und es funktioniert einwandfrei.
Kevin Day
Ein altes Problem, aber für mich scheint dies ein guter Hack zu sein. Zurück auf Null gestimmt :)
Tuukka Mustonen
Upvoted, da dies die einzige hier aufgeführte Lösung für den Fall ist, dass eine Klasse keine hat CodeSource.
Stellen Sie Monica 2331977
7

Ich habe die Antwort von acheron55 auf Java 7 portiert und das FileSystemObjekt geschlossen . Dieser Code funktioniert in IDEs, in JAR-Dateien und in einem JAR in einem Krieg gegen Tomcat 7. Beachten Sie jedoch, dass es in einem Glas in einem Krieg gegen JBoss 7 nicht funktioniert ( FileSystemNotFoundException: Provider "vfs" not installedsiehe auch diesen Beitrag ). Darüber hinaus ist es wie der ursprüngliche Code nicht threadsicher, wie von errr vorgeschlagen . Aus diesen Gründen habe ich diese Lösung aufgegeben; Wenn Sie diese Probleme jedoch akzeptieren können, finden Sie hier meinen vorgefertigten 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;
                }
            });
        }
    }
}
Pino
quelle
5

Hier ist eine Methode, die ich für "Alle JUnits unter einem Paket ausführen" geschrieben habe. Sie sollten in der Lage sein, es an Ihre Bedürfnisse anzupassen.

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 diesem Fall möchten Sie vielleicht auch dieses Snippet (gleicher Anwendungsfall :))

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
quelle
1
Ich denke, das große Problem ist, den JAR-Dateinamen an erster Stelle zu kennen. Es ist das Glas, in dem die Hauptklasse lebt.
OscarRyz
5

Hier ist ein Beispiel für die Verwendung der Reflections- Bibliothek zum rekursiven Scannen des Klassenpfads anhand eines Regex-Namensmusters, das mit einigen Guava- Vorteilen erweitert wurde, um Ressourceninhalte abzurufen:

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

Dies funktioniert sowohl mit Gläsern als auch mit explodierten Klassen.

Vadzim
quelle
Beachten Sie, dass Reflexionen Java 9 und höher immer noch nicht unterstützen: github.com/ronmamo/reflections/issues/186 . Dort gibt es Links zu konkurrierenden Bibliotheken.
Vadzim
3

Eine JAR-Datei ist nur eine Zip-Datei mit einem strukturierten Manifest. Sie können die JAR-Datei mit den üblichen Java-Zip-Tools öffnen und den Dateiinhalt auf diese Weise scannen, Streams aufblasen usw. Verwenden Sie dies dann in einem getResourceAsStream-Aufruf, und es sollte alles gut gelaunt sein.

EDIT / nach Klarstellung

Ich habe eine Minute gebraucht, um mich an all die Kleinigkeiten zu erinnern, und ich bin mir sicher, dass es sauberere Möglichkeiten gibt, aber ich wollte sehen, dass ich nicht verrückt bin. In meinem Projekt ist image.jpg eine Datei in einem Teil der Haupt-JAR-Datei. Ich erhalte den Klassenlader der Hauptklasse (SomeClass ist der Einstiegspunkt) und verwende ihn, um die Ressource image.jpg zu ermitteln. Dann etwas Stream-Magie, um es in dieses ImageInputStream-Ding zu bekommen, und alles ist in Ordnung.

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
quelle
Dieses Glas enthält das Hauptprogramm und die Ressourcen. Wie beziehe ich mich auf das Selbstglas? aus der JAR-Datei?
OscarRyz
Um auf die JAR-Datei zu verweisen, verwenden Sie einfach "blah.JAR" als Zeichenfolge. Sie können new File("blah.JAR")damit beispielsweise ein Dateiobjekt erstellen, das die JAR darstellt. Ersetzen Sie einfach "blah.JAR" durch den Namen Ihres JAR.
Thomas Owens
Wenn es dasselbe Glas ist, das Ihnen bereits ausgeht, sollte der Klassenlader in der Lage sein, Dinge im Glas zu sehen ... Ich habe falsch verstanden, was Sie ursprünglich versucht haben.
Mikeb
2
Nun ja, das habe ich schon, das Problem ist, wenn ich etwas brauche wie: "... getResourceAsStream (" *. Jpg "); ..." Das heißt, dynamisch die enthaltenen Dateien auflisten.
OscarRyz
3

Bei einer tatsächlichen JAR-Datei können Sie den Inhalt mit auflisten JarFile.entries(). Sie müssen jedoch den Speicherort der JAR-Datei kennen - Sie können den Klassenladeprogramm nicht einfach bitten, alles aufzulisten, worauf es ankommen könnte.

Sie sollten in der Lage sein, den Speicherort der JAR-Datei anhand der von zurückgegebenen URL zu ermitteln ThisClassName.class.getResource("ThisClassName.class"), aber es kann ein wenig umständlich sein.

Jon Skeet
quelle
Lesen Sie Ihre Antwort eine andere Frage aufgeworfen. Was würde den Aufruf ergeben: this.getClass (). GetResource ("/ my_directory"); Es sollte eine URL zurückgeben, die wiederum als Verzeichnis verwendet werden könnte. Nahh ... lass es mich versuchen.
OscarRyz
Sie kennen immer den Standort des JAR - es ist in ".". Solange bekannt ist, dass der Name der JAR etwas ist, können Sie irgendwo eine String-Konstante verwenden. Nun, wenn Leute den Namen des JAR ändern ...
Thomas Owens
@Thomas: Vorausgesetzt, Sie führen die App aus dem aktuellen Verzeichnis aus. Was ist los mit "java -jar foo / bar / baz.jar"?
Jon Skeet
Ich glaube (und müsste überprüfen), dass new File("baz.jar)das File-Objekt Ihre JAR-Datei darstellen würde , wenn Sie Code in Ihrem Jar hätten.
Thomas Owens
@ Thomas: Ich glaube nicht. Ich glaube, es wäre relativ zum aktuellen Arbeitsverzeichnis des Prozesses. Ich müsste aber auch nachsehen :)
Jon Skeet
3

Vor einiger Zeit habe ich eine Funktion erstellt, die innerhalb von JAR klasse wird:

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
quelle
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.
quelle
3
Dieses Codefragment kann das Problem zwar lösen, erklärt jedoch nicht, warum oder wie es die Frage beantwortet. Bitte geben Sie eine Erklärung für Ihren Code an , da dies wirklich zur Verbesserung der Qualität Ihres Beitrags beiträgt. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
Samuel Philipp
Daten sind leer, wenn wir den Code aus einer JAR-Datei ausführen.
Aguid
1

Der robusteste Mechanismus zum Auflisten aller Ressourcen im Klassenpfad ist derzeit die Verwendung dieses Musters mit ClassGraph , da es die größtmögliche Anzahl von Klassenpfadspezifikationsmechanismen verarbeitet , einschließlich des neuen JPMS-Modulsystems. (Ich bin der Autor von ClassGraph.)

Woher weiß ich den Namen der JAR-Datei, in der meine Hauptklasse lebt?

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

Wie kann ich den Inhalt eines Verzeichnisses in einer JAR-Datei auf ähnliche Weise lesen?

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

Es gibt auch viele andere Möglichkeiten, mit Ressourcen umzugehen .

Luke Hutchison
quelle
1
Sehr schönes Paket, leicht in meinem Scala-Projekt verwendbar, danke.
Zslim
0

Nur eine andere Art, Dateien von einer JAR-URL aufzulisten / zu lesen, und dies rekursiv für verschachtelte 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
quelle
0

Noch eine für die Straße:

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

Dies ist etwas flexibler für das Abgleichen bestimmter Dateinamen, da Wildcard-Globbing verwendet wird.


Ein funktionalerer Stil:

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
quelle