Comment puis-je obtenir un «dossier» de ressources depuis mon fichier jar?

139

J'ai un dossier / package de ressources à la racine de mon projet, je ne veux pas charger un certain fichier. Si je voulais charger un certain fichier, j'utiliserais class.getResourceAsStream et tout irait bien !! Ce que je veux en fait, c'est charger un «dossier» dans le dossier des ressources, faire une boucle sur les fichiers à l'intérieur de ce dossier et obtenir un flux vers chaque fichier et lire dans le contenu ... Supposons que les noms de fichiers ne sont pas déterminés avant l'exécution ... Que devrais-je faire? Existe-t-il un moyen d'obtenir une liste des fichiers dans un dossier dans votre fichier jar? Notez que le fichier Jar avec les ressources est le même fichier jar à partir duquel le code est exécuté ...

Merci d'avance...

Mostafa Zeinali
la source
1
Cela aiderait: stackoverflow.com/questions/1529611/…
Jigar Joshi
Voir aussi: stackoverflow.com/questions/4764347/…
Jigar Joshi
5
Le premier concerne l'extraction d'un fichier jar, ce qui est un peu la dernière chose que je veux essayer, comme une situation si-pire-devient-pire !! Si je veux que les fichiers soient extraits, je les mettrais simplement dans mon dossier d'installation au moment de l'installation! Je veux y accéder depuis l'intérieur du jar, et ma raison est que si getResourceAsStream peut accéder à un fichier, Java devrait également être en mesure d'explorer les dossiers de ce jar, sans avoir besoin de l'extraire !! Mais merci ...
Mostafa Zeinali

Réponses:

111

Enfin, j'ai trouvé la solution:

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

Le deuxième bloc fonctionne uniquement lorsque vous exécutez l'application sur l'IDE (pas avec le fichier jar), vous pouvez le supprimer si vous n'aimez pas cela.

user1079877
la source
Mise à jour de la bonne réponse. Bien que ce ne soit pas ce que je voudrais faire, surtout dans un code avec plus de 34000 fichiers sources ... Mais cela semble être la seule solution. Je vous remercie.
Mostafa Zeinali
4
Pour info, cela ne fonctionne pas s'il est lancé à partir de webstart car getPath renvoie quelque chose de relatif au domaine du serveur.
amos
1
Ne pensez pas que cela fonctionnera si le chemin est dans un fichier jar, donnera cette erreur sur la conversion toURI: java.lang.IllegalArgumentException: URI n'est pas hiérarchique
tribbloid
4
Fonctionne
2
Ce n'est pas une solution sûre, car: 1. Il suppose que le fichier .jar est un fichier local sur le même système; 2. URL.getPath () ne renvoie pas de nom de fichier valide, il renvoie simplement la partie chemin de l'URL, avec tous les pourcentages d'échappement intacts; 3. ProtectionDomain.getCodeSource () peut renvoyer null .
VGR
14

Essayez ce qui suit.
Créez le chemin de la ressource "<PathRelativeToThisClassFile>/<ResourceDirectory>" Par exemple, si votre chemin de classe est com.abc.package.MyClass et que vos fichiers de ressources sont dans 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
    }
}

Vous pouvez aussi utiliser

URL url = MyClass.class.getResource("/com/abc/package/resources/");
Glen Best
la source
27
Merci pour le temps et les efforts, mais cela ne fonctionne PAS lorsque votre code base dans un fichier .jar et vos ressources sont également dans ce fichier .jar. Lorsque vous êtes dans un fichier .jar, vous ne pouvez pas créer un fichier () et vous ne pouvez pas non plus obtenir une URL pour ce dossier ... Je suis personnellement venu en paix avec lui et je viens de lister chaque fichier dans un XML ... Je ne le fais pas ' Je ne pense pas que vous puissiez le faire pour un dossier dans un fichier jar ...
Mostafa Zeinali
2
Désolé que vous ayez des problèmes. Cependant, cela fonctionne réellement lorsque votre base de code est dans un fichier jar et que vos ressources sont également dans ce fichier jar. Vous ne créez pas un fichier () sur le disque - vous lisez le fichier dans le fichier jar dans la mémoire java. Notez que le ClassLoader est assez heureux de traiter un fichier jar équivalent à un répertoire sur disque. Voici les détails de la source:
Glen Best
1
J'ai juste exécuté le code sur un bocal et j'ai obtenu l'erreur ... oups. V désolé, mon mal. Vous devez gérer le chemin de l'URL du fichier jar avec "Fichier: ...! / Dir1 / dir2 ...". Deux correctifs: stackoverflow.com/questions/1429172/… OU Chaîne jarPath = dirURL.getPath (). Substring (5, dirURL.getPath (). IndexOf ("!")); JarFile jar = nouveau JarFile (URLDecoder.decode (jarPath, "UTF-8")); Énumération <JarEntry> entries = jar.entries (); while (entries.hasMoreElements ()) {// faire quelque chose}
Glen Best
3
Depuis le javadoc, il est évident que la méthode n'est pas TOUJOURS garantie de retourner un fichier dans toutes les circonstances. MAIS, si vous lisez réellement le Q, il est garanti à 100% que l'OP fonctionne avec des fichiers et des dossiers par construction. Le Q demande l'accès aux répertoires et aux fichiers - c'est plus spécifique que l'abstraction généralisée de l'API. La conversion en fichier est appropriée et correcte pour répondre aux besoins. Veuillez lire le Q correctement avant de publier des commentaires fortement contraires. À votre santé.
Glen Best
7
L'OP ne fonctionne pas avec les fichiers et les répertoires. Il travaille avec des ressources dans un fichier JAR. Les ressources d'un fichier JAR ne sont pas des fichiers ou des répertoires et ne peuvent pas être répertoriées avec la Fileclasse. Ce code ne fonctionne pas avec les ressources dans un fichier JAR. Période.
Marquis of Lorne
7

Je sais que cela remonte à plusieurs années. Mais juste pour d'autres personnes rencontrent ce sujet. Ce que vous pouvez faire est d'utiliser la getResourceAsStream()méthode avec le chemin du répertoire, et le flux d'entrée aura tous les noms de fichiers de ce répertoire. Après cela, vous pouvez concater le chemin du répertoire avec chaque nom de fichier et appeler getResourceAsStream pour chaque fichier d'une boucle.

Cheng Lin
la source
7
Cela fonctionne bien à moins que vous ne prévoyiez de contrarier les choses, en fin de compte.
Tustin2121
1
Je pense que cela devrait être la solution acceptée car elle est propre et ne nécessite pas de manipulation des versions jar et IDE dans des cas séparés. Bien que je ne sache pas pourquoi getResource ne trouve pas le dossier, mais getResourceAsStream le fait.
Raghuram Onti Srinivasan
6

J'ai eu le même problème entre les mains pendant que j'essayais de charger certaines configurations hadoop à partir de ressources emballées dans le jar ... à la fois sur l'IDE et sur le jar (version finale).

J'ai trouvé que java.nio.file.DirectoryStreamc'était le meilleur moyen d'itérer le contenu du répertoire sur le système de fichiers local et le fichier 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());
    }
}
SQL.injection
la source
CA marchait bien pour moi. Seulement j'ai changé le 'Path jarFile = Paths.get (jar.toString (). Substring ("file:". Length ()));' avec 'Path jarFile = Paths.get (jar.toURI ());' pour le faire fonctionner sous Windows. Également utilisé une instruction try with resource pour l'objet FileSystem.
Jarle Jacobsen
Cela a fonctionné pour moi! J'exécutais l'application dropwizard dans docker.
nIKUNJ le
4

Le code suivant renvoie le «dossier» souhaité comme chemin, qu'il se trouve ou non dans un fichier jar.

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

Nécessite java 7+.

bourdonnementV
la source
Agréable! Je dois ajouter cependant que FileSystem est fermable, ce qui signifie que vous devez le fermer à un moment donné pour libérer des ressources système. Bien sûr, le chemin retourné deviendra invalide immédiatement après cela.
Kirill Gamazkov
Notez que dans le cas d'un fichier jar, le nom de la ressource (ici package + folder) semble être ignoré lors de la création du système de fichiers. Par conséquent, getPathne revient pas folder/path/to/..., mais seulement path/to/....
Marcono1234
1

Une autre solution, vous pouvez le faire en utilisant ResourceLoadercomme ceci:

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())
}
Qy Zuo
la source
0

Simple ... utilisez OSGi. Dans OSGi, vous pouvez parcourir les entrées de votre Bundle avec findEntries et findPaths.

Peter Kriens
la source
10
Si cela implique OSGI, je l'appellerais à peine «simple». Mais si vous utilisez déjà OSGI ... oui, bien sûr. Mais suggérer de passer à OSGI juste pour résoudre ce problème particulier semble un peu trop.
Kris le
OSGi résout également de nombreux autres problèmes et de nos jours, il est très facile de se lancer. Voir les tutoriels OSGi enRoute
Peter Kriens
Je resterais loin d'OSGi à moins que vous ne soyez déjà obligé de l'utiliser. Bloatware sur-conçu qui résout les problèmes de déploiement de l'OMI des années 90.
user2337270
-1

Dans mon fichier jar, j'avais un dossier appelé Upload, ce dossier contenait trois autres fichiers texte et je devais avoir exactement le même dossier et des fichiers en dehors du fichier jar, j'ai utilisé le code ci-dessous:

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);
AmourBelleJava
la source
1
Salut, merci pour la réponse, mais, vous voyez, vous aviez besoin de connaître le nom de chaque fichier dans ce dossier lors de l'écriture du code; et vous deviez nommer chacun d'eux explicitement. Ce que je cherchais, c'était une méthode pour itérer sur les fichiers à l'intérieur du dossier et obtenir leurs noms à l'exécution, afin que je puisse ajouter des ressources au dossier, sans changer le code, sachant que le code les traitera également. Merci pour votre temps. : 3
Mostafa Zeinali
-1

Comme le soulignent les autres réponses, une fois que les ressources sont dans un fichier jar, les choses deviennent vraiment laides. Dans notre cas, cette solution:

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

fonctionne très bien dans les tests (car lorsque les tests sont exécutés, le code n'est pas emballé dans un fichier jar), mais ne fonctionne pas lorsque l'application fonctionne normalement. Donc, ce que j'ai fait, c'est ... Je code en dur la liste des fichiers dans l'application, mais j'ai un test qui lit la liste réelle à partir du disque (peut le faire puisque cela fonctionne dans les tests) et échoue si la liste réelle ne fonctionne pas ne correspond pas à la liste renvoyée par l'application.

De cette façon, j'ai du code simple dans mon application (pas de trucs), et je suis sûr que je n'ai pas oublié d'ajouter une nouvelle entrée dans la liste grâce au test.

Emmanuel Touzery
la source
-25

Ce lien vous explique comment.

La magie est la méthode getResourceAsStream ():

InputStream is = 
this.getClass().getClassLoader().getResourceAsStream("yourpackage/mypackage/myfile.xml")
James Anderson
la source
20
Non mec!! Je ne veux pas un seul fichier !! Je veux un dossier entier, dont les sous-fichiers sont indéterminés avant l'exécution !!!
Mostafa Zeinali