Wie kann ich eine Ressource "Ordner" aus meiner JAR-Datei abrufen?


139

Ich habe einen Ressourcenordner / ein Ressourcenpaket im Stammverzeichnis meines Projekts. Ich möchte eine bestimmte Datei "nicht" laden. Wenn ich eine bestimmte Datei laden wollte, würde ich class.getResourceAsStream verwenden und mir würde es gut gehen !! Was ich eigentlich tun möchte, ist, einen "Ordner" in den Ressourcenordner zu laden, die Dateien in diesem Ordner zu durchlaufen und einen Stream zu jeder Datei zu erhalten und den Inhalt einzulesen ... Angenommen, die Dateinamen werden nicht vor der Laufzeit bestimmt ... Was soll ich machen? Gibt es eine Möglichkeit, eine Liste der Dateien in einem Ordner in Ihrer JAR-Datei abzurufen? Beachten Sie, dass die Jar-Datei mit den Ressourcen dieselbe JAR-Datei ist, aus der der Code ausgeführt wird ...

Danke im Voraus...




5
Bei der ersten geht es darum, eine JAR-Datei zu extrahieren, was das Letzte ist, was ich versuchen möchte, wie eine Situation, in der es noch schlimmer wird !! Wenn ich möchte, dass die Dateien extrahiert werden, lege ich sie zur Installationszeit einfach in meinen Installationsordner! Ich möchte von der JAR-Datei aus auf sie zugreifen. Wenn getResourceAsStream auf eine Datei zugreifen kann, sollte Java auch in der Lage sein, Ordner in dieser JAR-Datei zu durchsuchen, ohne sie extrahieren zu müssen. Aber danke ...
Mostafa Zeinali

Antworten:


111

Schließlich fand ich die Lösung:

final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

if(jarFile.isFile()) {  // Run with JAR file
    final JarFile jar = new JarFile(jarFile);
    final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
    while(entries.hasMoreElements()) {
        final String name = entries.nextElement().getName();
        if (name.startsWith(path + "/")) { //filter according to the path
            System.out.println(name);
        }
    }
    jar.close();
} else { // Run with IDE
    final URL url = Launcher.class.getResource("/" + path);
    if (url != null) {
        try {
            final File apps = new File(url.toURI());
            for (File app : apps.listFiles()) {
                System.out.println(app);
            }
        } catch (URISyntaxException ex) {
            // never happens
        }
    }
}

Der zweite Block funktioniert nur, wenn Sie die Anwendung auf IDE ausführen (nicht mit JAR-Datei). Sie können ihn entfernen, wenn Ihnen das nicht gefällt.


Die richtige Antwort wurde aktualisiert. Dies ist zwar nicht das, was ich tun möchte, insbesondere in einem Code mit mehr als 34000 Quelldateien ... Aber es scheint die einzige Lösung zu sein. Danke dir.
Mostafa Zeinali

4
Zu Ihrer Information, dies funktioniert nicht, wenn es von Webstart aus gestartet wird, da getPath etwas relativ zur Serverdomäne zurückgibt.
Amos

1
Denken Sie nicht, dass es funktionieren wird, wenn sich der Pfad in einem JAR befindet. Bei der TOURI-Konvertierung wird dieser Fehler angezeigt: java.lang.IllegalArgumentException: URI ist nicht hierarchisch
Tribbloid

4
Funktioniert nur für Single-JAR-Dateien, funktioniert nicht zum Extrahieren von Ressourcen aus Abhängigkeiten in einem anderen JAR
Tribbloid

2
Dies ist keine sichere Lösung, da: 1. davon ausgegangen wird, dass die JAR-Datei eine lokale Datei auf demselben System ist; 2. URL.getPath () gibt keinen gültigen Dateinamen zurück, sondern nur den Pfadabschnitt der URL, wobei alle Prozent-Escapezeichen intakt sind. 3. ProtectionDomain.getCodeSource () kann null zurückgeben .
VGR

14

Versuche Folgendes. Erstellen
Sie den Ressourcenpfad. "<PathRelativeToThisClassFile>/<ResourceDirectory>"Wenn Ihr Klassenpfad beispielsweise com.abc.package.MyClass lautet und sich Ihre Ressourcendateien in src / com / abc / package / resources / befinden:

URL url = MyClass.class.getResource("resources/");
if (url == null) {
     // error - missing folder
} else {
    File dir = new File(url.toURI());
    for (File nextFile : dir.listFiles()) {
        // Do something with nextFile
    }
}

Sie können auch verwenden

URL url = MyClass.class.getResource("/com/abc/package/resources/");

27
Vielen Dank für die Zeit und Mühe, aber dies funktioniert NICHT, wenn sich Ihr Code in einer JAR-Datei befindet und sich Ihre Ressourcen auch in dieser JAR-Datei befinden. Wenn Sie sich in einer JAR-Datei befinden, können Sie keine Datei () erstellen und Sie können auch keine URL für diesen Ordner erhalten ... Ich persönlich bin damit einverstanden und habe einfach jede Datei in einem XML aufgelistet ... Ich weiß nicht. Ich glaube nicht, dass Sie das für einen Ordner in einer JAR-Datei tun können ...
Mostafa Zeinali

2
Entschuldigung, dass Sie Probleme haben. Es funktioniert jedoch tatsächlich, wenn sich Ihre Codebasis in einer JAR-Datei befindet und sich Ihre Ressourcen auch in dieser JAR-Datei befinden. Sie erstellen keine Datei () auf der Festplatte - Sie lesen die Datei in der JAR-Datei in den Java-Speicher. Beachten Sie, dass der ClassLoader sehr gerne eine JAR-Datei behandelt, die einem Verzeichnis auf der Festplatte entspricht. Hier sind die Details aus der Quelle:
Glen Best

1
Ich habe gerade den Code in einem Glas ausgeführt und den Fehler erhalten ... oops. V Entschuldigung, mein schlechtes. Der JAR-URL-Pfad muss mit "Datei: ...! / Dir1 / dir2 ..." behandelt werden. Zwei Korrekturen: stackoverflow.com/questions/1429172/… OR String jarPath = dirURL.getPath (). Teilstring (5, dirURL.getPath (). IndexOf ("!")); JarFile jar = neues JarFile (URLDecoder.decode (jarPath, "UTF-8")); Aufzählung <JarEntry> entry = jar.entries (); while (entry.hasMoreElements ()) {// etwas tun}
Glen Best

3
Aus dem Javadoc geht hervor, dass die Methode nicht IMMER garantiert, dass eine Datei unter allen Umständen zurückgegeben wird. ABER, wenn Sie das Q tatsächlich lesen, ist garantiert, dass das OP zu 100% konstruktionsbedingt mit Dateien und Ordnern arbeitet. Das Q bittet um Zugriff auf Verzeichnisse und Dateien - dies ist spezifischer als die verallgemeinerte Abstraktion der API. Die Konvertierung in eine Datei ist angemessen und korrekt, um den Anforderungen gerecht zu werden. Bitte lesen Sie das Q richtig durch, bevor Sie solche stark gegensätzlichen Kommentare veröffentlichen. Prost.
Glen Best

7
Das OP funktioniert nicht mit Dateien und Verzeichnissen. Er arbeitet mit Ressourcen in einer JAR-Datei. Ressourcen in einer JAR-Datei sind keine Dateien oder Verzeichnisse und können nicht mit der FileKlasse aufgelistet werden . Dieser Code funktioniert nicht mit Ressourcen in einer JAR-Datei. Zeitraum.
Marquis von Lorne

7

Ich weiß, das ist viele Jahre her. Aber nur für andere Leute stoßen Sie auf dieses Thema. Sie können eine getResourceAsStream()Methode mit dem Verzeichnispfad verwenden, und der Eingabestream enthält alle Dateinamen aus diesem Verzeichnis. Danach können Sie den Verzeichnispfad mit jedem Dateinamen verknüpfen und getResourceAsStream für jede Datei in einer Schleife aufrufen.


7
Dies funktioniert gut, es sei denn, Sie planen, die Dinge durcheinander zu bringen, wie sich herausstellt.
Tustin2121

1
Ich denke, dies sollte die akzeptierte Lösung sein, da sie sauber ist und keine Behandlung von JAR- und IDE-Versionen in separaten Fällen erfordert. Ich bin mir zwar nicht sicher, warum getResource den Ordner nicht findet, getResourceAsStream jedoch.
Raghuram Onti Srinivasan

5

Ich hatte das gleiche Problem, als ich versuchte, einige Hadoop-Konfigurationen von Ressourcen zu laden, die in der JAR gepackt waren ... sowohl auf der IDE als auch auf der JAR (Release-Version).

Ich fand java.nio.file.DirectoryStreames am besten, Verzeichnisinhalte sowohl über das lokale Dateisystem als auch über das JAR zu durchlaufen.

String fooFolder = "/foo/folder";
....

ClassLoader classLoader = foofClass.class.getClassLoader();
try {
    uri = classLoader.getResource(fooFolder).toURI();
} catch (URISyntaxException e) {
    throw new FooException(e.getMessage());
} catch (NullPointerException e){
    throw new FooException(e.getMessage());
}

if(uri == null){
    throw new FooException("something is wrong directory or files missing");
}

/** i want to know if i am inside the jar or working on the IDE*/
if(uri.getScheme().contains("jar")){
    /** jar case */
    try{
        URL jar = FooClass.class.getProtectionDomain().getCodeSource().getLocation();
        //jar.toString() begins with file:
        //i want to trim it out...
        Path jarFile = Paths.get(jar.toString().substring("file:".length()));
        FileSystem fs = FileSystems.newFileSystem(jarFile, null);
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(fs.getPath(fooFolder));
        for(Path p: directoryStream){
            InputStream is = FooClass.class.getResourceAsStream(p.toString()) ;
        performFooOverInputStream(is);
        /** your logic here **/
            }
    }catch(IOException e) {
        throw new FooException(e.getMessage());     
    }
}
else{
    /** IDE case */
    Path path = Paths.get(uri);
    try {
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
        for(Path p : directoryStream){
            InputStream is = new FileInputStream(p.toFile());
            performFooOverInputStream(is);
        }
    } catch (IOException _e) {
        throw new FooException(_e.getMessage());
    }
}

Das hat bei mir gut funktioniert. Nur ich habe den 'Path jarFile = Paths.get (jar.toString (). Substring ("file:". Length ()));' mit 'Path jarFile = Paths.get (jar.toURI ());' damit es unter Windows funktioniert. Verwendete auch einen Versuch mit Ressourcenanweisung für das FileSystem-Objekt.
Jarle Jacobsen

4

Der folgende Code gibt den gewünschten "Ordner" als Pfad zurück, unabhängig davon, ob er sich in einem Glas befindet oder nicht.

  private Path getFolderPath() throws URISyntaxException, IOException {
    URI uri = getClass().getClassLoader().getResource("folder").toURI();
    if ("jar".equals(uri.getScheme())) {
      FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap(), null);
      return fileSystem.getPath("path/to/folder/inside/jar");
    } else {
      return Paths.get(uri);
    }
  }

Benötigt Java 7+.


Nett! Ich muss jedoch hinzufügen, dass FileSystem geschlossen werden kann, was bedeutet, dass Sie es irgendwann schließen müssen, um Systemressourcen freizugeben. Natürlich wird der zurückgegebene Pfad sofort danach ungültig.
Kirill Gamazkov

Beachten Sie, dass bei einer JAR-Datei der Name der Ressource (hier Paket + folder) beim Erstellen des Dateisystems ignoriert zu werden scheint. Kommt getPathalso nicht zurück folder/path/to/..., sondern nur path/to/....
Marcono1234

1

Eine andere Lösung, die Sie folgendermaßen durchführen können ResourceLoader:

import org.springframework.core.io.Resource;
import org.apache.commons.io.FileUtils;

@Autowire
private ResourceLoader resourceLoader;

...

Resource resource = resourceLoader.getResource("classpath:/path/to/you/dir");
File file = resource.getFile();
Iterator<File> fi = FileUtils.iterateFiles(file, null, true);
while(fi.hasNext()) {
    load(fi.next())
}

0

Einfach ... benutze OSGi. In OSGi können Sie die Einträge Ihres Bundles mit findEntries und findPaths durchlaufen.


10
Wenn es sich um OSGI handelt, würde ich es kaum als "einfach" bezeichnen. Aber wenn Sie gerade OSGI verwenden ... ja sicher. Der Vorschlag, zu OSGI zu wechseln, um dieses spezielle Problem zu lösen, scheint jedoch etwas zu viel.
Kris

OSGi löst auch viele andere Probleme und heutzutage ist der Einstieg sehr einfach. Siehe die OSGi enRoute Tutorials
Peter Kriens

Ich würde mich von OSGi fernhalten, es sei denn, Sie müssen es bereits verwenden. Überentwickelte Bloatware, die die Bereitstellungsprobleme der IMO der 90er Jahre löst.
user2337270

-1

In meiner JAR-Datei befand sich ein Ordner namens "Hochladen". In diesem Ordner befanden sich drei weitere Textdateien. Ich musste genau denselben Ordner und dieselben Dateien außerhalb der JAR-Datei haben. Ich habe den folgenden Code verwendet:

URL inputUrl = getClass().getResource("/upload/blabla1.txt");
File dest1 = new File("upload/blabla1.txt");
FileUtils.copyURLToFile(inputUrl, dest1);

URL inputUrl2 = getClass().getResource("/upload/blabla2.txt");
File dest2 = new File("upload/blabla2.txt");
FileUtils.copyURLToFile(inputUrl2, dest2);

URL inputUrl3 = getClass().getResource("/upload/blabla3.txt");
File dest3 = new File("upload/Bblabla3.txt");
FileUtils.copyURLToFile(inputUrl3, dest3);

1
Hallo, danke für die Antwort, aber Sie sehen, Sie mussten den Namen jeder Datei in diesem Ordner kennen, wenn Sie den Code schreiben. und man musste jeden explizit benennen. Was ich suchte, war eine Methode zum Iterieren der Dateien im Ordner und zum Abrufen ihrer Namen zur Laufzeit, damit ich dem Ordner Ressourcen hinzufügen konnte, ohne den Code zu ändern, da ich wusste, dass der Code sie auch verarbeitet. Vielen Dank für Ihre Zeit. : 3
Mostafa Zeinali

-1

Wie die anderen Antworten zeigen, werden die Dinge wirklich hässlich, sobald sich die Ressourcen in einer JAR-Datei befinden. In unserem Fall ist diese Lösung:

https://stackoverflow.com/a/13227570/516188

funktioniert sehr gut in den Tests (da der Code beim Ausführen der Tests nicht in eine JAR-Datei gepackt wird), funktioniert aber nicht, wenn die App tatsächlich normal ausgeführt wird. Also habe ich ... die Liste der Dateien in der App fest codiert, aber ich habe einen Test, der die tatsächliche Liste von der Festplatte liest (kann dies tun, da dies in Tests funktioniert) und fehlschlägt, wenn die tatsächliche Liste nicht funktioniert stimmt nicht mit der Liste überein, die die App zurückgibt.

Auf diese Weise habe ich einfachen Code in meiner App (keine Tricks), und ich bin sicher, ich habe dank des Tests nicht vergessen, einen neuen Eintrag in die Liste aufzunehmen.


-25

Dieser Link zeigt Ihnen, wie.

Die Magie ist die Methode getResourceAsStream ():

InputStream is = 
this.getClass().getClassLoader().getResourceAsStream("yourpackage/mypackage/myfile.xml")

20
Kein Typ !! Ich will keine einzige Datei !! Ich möchte einen ganzen Ordner, dessen Unterdateien vor der Laufzeit unbestimmt sind !!!
Mostafa Zeinali
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.