Come posso ottenere una "cartella" di risorse dall'interno del mio file jar?


139

Ho una cartella / pacchetto di risorse nella radice del mio progetto, "non" voglio caricare un determinato file. Se volessi caricare un certo file, userei class.getResourceAsStream e andrei bene !! Quello che voglio effettivamente fare è caricare una "Cartella" all'interno della cartella delle risorse, eseguire il ciclo sui File all'interno di quella Cartella e ottenere uno Stream su ciascun file e leggere il contenuto ... Supponiamo che i nomi dei File non siano determinati prima del runtime ... Cosa dovrei fare? C'è un modo per ottenere un elenco dei file all'interno di una cartella nel file jar? Si noti che il file Jar con le risorse è lo stesso file jar da cui viene eseguito il codice ...

Grazie in anticipo...




5
Il primo riguarda l'estrazione di un file jar, che è una specie dell'ultima cosa che voglio provare, come una situazione peggiore! Se volessi estrarre i file, li metterei semplicemente nella mia cartella di installazione al momento dell'installazione! Voglio accedervi dall'interno del vaso, e la mia ragione è che se getResourceAsStream può accedere a un file, Java dovrebbe anche essere in grado di esplorare le cartelle in quel vaso, senza la necessità di estrarlo !! Ma grazie ...
Mostafa Zeinali

Risposte:


111

Alla fine ho trovato la soluzione:

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

Il secondo blocco funziona solo quando esegui l'applicazione su IDE (non con file jar), puoi rimuoverlo se non ti piace.


Aggiornata la risposta corretta. Anche se questo non è ciò che vorrei fare, specialmente in un codice con oltre 34000 file sorgente ... Ma sembra essere l'unica soluzione. Grazie.
Mostafa Zeinali

4
Cordiali saluti, questo non funziona se avviato da webstart perché getPath restituisce qualcosa relativo al dominio del server.
amos,

1
Non pensare che funzionerà se il percorso è in un barattolo, darà questo errore alla conversione inURI: java.lang.IllegalArgumentException: URI non è gerarchico
tribbloid

4
Funziona solo con file single-jar, non funziona per estrarre risorse dalle dipendenze in un altro jar
tribbloid,

2
Questa non è una soluzione sicura, perché: 1. Presuppone che il file .jar sia un file locale sullo stesso sistema; 2. URL.getPath () non restituisce un nome file valido, restituisce solo la parte del percorso dell'URL, con tutte le escape di percentuale intatte; 3. ProtectionDomain.getCodeSource () può restituire null .
VGR,

14

Prova quanto segue.
Crea il percorso delle risorse "<PathRelativeToThisClassFile>/<ResourceDirectory>" Ad esempio se il percorso della tua classe è com.abc.package.MyClass e i tuoi file di risoluzione sono all'interno di src / com / abc / package / resources /:

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

Puoi anche usare

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

27
Grazie per il tempo e lo sforzo, ma questo NON funziona quando la tua base di codice si trova all'interno di un file .jar e anche le tue risorse sono in quel file .jar. Quando sei all'interno di un file .jar, non puoi creare un File () e non puoi nemmeno ottenere un URL per quella cartella ... Sono venuto personalmente in pace con esso e ho appena elencato ogni file in un XML ... non credo che tu possa farlo per una cartella all'interno di un file jar ...
Mostafa Zeinali

2
Mi dispiace che tu abbia problemi. Tuttavia, in realtà funziona quando la tua base di codice si trova in un file jar e anche le tue risorse sono in quel file jar. Non stai creando un File () su disco - stai leggendo il File all'interno del file jar nella memoria java. Si noti che ClassLoader è abbastanza felice di trattare un file jar equivalente a una directory sul disco. Ecco i dettagli dalla fonte:
Glen Best

1
Ho appena eseguito il codice su un vaso e ho ricevuto l'errore ... oops. Mi dispiace, mia cattiva. È necessario gestire il percorso URL jar con "File: ...! / Dir1 / dir2 ...". Due correzioni: stackoverflow.com/questions/1429172/... . O String = jarPath dirURL.getPath () substring (. "" 5, dirURL.getPath () indexOf ()); JarFile jar = new JarFile (URLDecoder.decode (jarPath, "UTF-8")); Enumerazione <JarEntry> entry = jar.entries (); while (entry.hasMoreElements ()) {// do qualcosa}
Glen Best

3
Dal javadoc è ovvio che il metodo non è SEMPRE garantito per restituire un file in tutte le circostanze. MA, se in realtà leggi la Q, l'OP è garantito al 100% per funzionare con file e cartelle per costruzione. La Q chiede di accedere a directory e file: questo è più specifico dell'astrazione generalizzata dell'API. La conversione in file è appropriata e corretta per soddisfare l'esigenza. Si prega di leggere correttamente la Q prima di pubblicare commenti così fortemente contrari. Saluti.
Glen Best,

7
L'OP non funziona con file e directory. Sta lavorando con risorse in un file JAR. Le risorse in un file JAR non sono file o directory e non possono essere elencate con la Fileclasse. Questo codice non funziona con le risorse all'interno di un file JAR. Periodo.
Marchese di Lorne,

7

So che è molti anni fa. Ma solo per le altre persone si imbattono in questo argomento. Quello che potresti fare è usare il getResourceAsStream()metodo con il percorso della directory, e lo Stream di input avrà tutti i nomi dei file da quella directory. Successivamente è possibile concaticare il percorso dir con ciascun nome di file e chiamare getResourceAsStream per ciascun file in un ciclo.


7
Funziona bene a meno che tu non abbia intenzione di scuotere le cose, come risulta.
Tustin2121,

1
Penso che questa dovrebbe essere la soluzione accettata poiché è pulita e non richiede la gestione di versioni jar e IDE in casi separati. Anche se non sono sicuro del motivo per cui getResource non trova la cartella, ma getResourceAsStream lo fa.
Raghuram Onti Srinivasan,

6

Ho avuto lo stesso problema a portata di mano mentre stavo tentando di caricare alcune configurazioni hadoop dalle risorse contenute nel vaso ... sia sull'IDE che sul vaso (versione di rilascio).

Ho scoperto java.nio.file.DirectoryStreamdi funzionare al meglio per scorrere i contenuti della directory sia sul filesystem locale che sul jar.

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

Questo ha funzionato bene per me. Solo io ho cambiato 'Path jarFile = Paths.get (jar.toString (). Substring ("file:". Length ()));' con 'Path jarFile = Paths.get (jar.toURI ());' per farlo funzionare in Windows. Utilizzato anche un tentativo con l'istruzione di risorsa per l'oggetto FileSystem.
Jarle Jacobsen,

Questo ha funzionato per me! Stavo eseguendo l'applicazione Dropwizard nella finestra mobile.
nIKUNJ,

4

Il codice seguente restituisce la "cartella" desiderata come Percorso indipendentemente dal fatto che si trovi all'interno di un vaso o meno.

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

Richiede java 7+.


Bello! Devo aggiungere però che FileSystem è Chiudibile, il che significa che ad un certo punto è necessario chiuderlo per liberare risorse di sistema. Naturalmente, il percorso restituito non sarà più valido immediatamente dopo.
Kirill Gamazkov,

Si noti che nel caso di un file jar, il nome della risorsa (qui pacchetto + folder) sembra essere ignorato durante la creazione del file system. Pertanto getPathnon ritorna folder/path/to/..., ma solo path/to/....
Marcono1234

1

Un'altra soluzione, puoi farlo usando ResourceLoadercosì:

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

Semplice ... usa OSGi. In OSGi puoi scorrere le voci del tuo pacchetto con findEntries e findPaths.


10
Se coinvolge OSGI difficilmente lo definirei "semplice". Ma se ti capita già di usare OSGI ... sì certo. Ma suggerire di passare all'OSGI solo per risolvere questo particolare problema sembra un po 'troppo.
Kris,

OSGi risolve anche molti altri problemi e oggigiorno è molto facile iniziare. Guarda i tutorial di OSGi enRoute
Peter Kriens,

Starei lontano da OSGi a meno che non ti sia già richiesto di usarlo. Bloatware eccessivamente progettato che risolve i problemi di implementazione dell'IMO degli anni '90.
user2337270,

-1

All'interno del mio file jar avevo una cartella chiamata Upload, questa cartella aveva tre altri file di testo al suo interno e avevo bisogno di avere esattamente la stessa cartella e file al di fuori del file jar, ho usato il codice qui sotto:

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
Ciao, grazie per la risposta, ma, vedi, hai bisogno di sapere il nome di ogni file all'interno di quella cartella quando scrivi il codice; e dovevi nominare ognuno in modo esplicito. Quello che stavo cercando era un metodo per scorrere i file all'interno della cartella e ottenere i loro nomi in fase di esecuzione, in modo da poter aggiungere risorse alla cartella, senza modificare il codice, sapendo che il codice li elaborerà anche. Grazie per il tuo tempo però. : 3
Mostafa Zeinali

-1

Come indicano le altre risposte, una volta che le risorse sono all'interno di un file jar, le cose diventano davvero brutte. Nel nostro caso, questa soluzione:

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

funziona molto bene nei test (poiché quando i test vengono eseguiti il ​​codice non è impacchettato in un file jar), ma non funziona quando l'app viene effettivamente eseguita normalmente. Quindi quello che ho fatto è ... Ho hardcode l'elenco dei file nell'app, ma ho un test che legge l'elenco effettivo dal disco (può farlo dal momento che funziona nei test) e fallisce se l'elenco effettivo non lo fa non corrisponde all'elenco restituito dall'app.

In questo modo ho un semplice codice nella mia app (nessun trucco) e sono sicuro di non aver dimenticato di aggiungere una nuova voce nell'elenco grazie al test.


-25

Questo link ti dice come.

La magia è il metodo getResourceAsStream ():

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

20
No amico !! NON voglio un singolo file !! Voglio un'intera cartella, i cui file secondari sono pre-runtime indeterminati !!!
Mostafa Zeinali
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.