184 votes

Comment obtenir le chemin d'accès à une ressource dans un fichier Java JAR ?

J'essaie d'obtenir un chemin vers une ressource mais je n'ai pas eu de chance.

Cela fonctionne (à la fois dans l'IDE et avec le JAR) mais de cette façon, je ne peux pas obtenir le chemin d'accès à un fichier, seulement le contenu du fichier :

ClassLoader classLoader = getClass().getClassLoader();
PrintInputStream(classLoader.getResourceAsStream("config/netclient.p"));

Si je fais ça :

ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("config/netclient.p").getFile());

Le résultat est : java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

Existe-t-il un moyen d'obtenir le chemin d'accès à un fichier de ressources ?

0 votes

Puis-je vous demander pourquoi vous avez besoin de ce chemin ?

1 votes

Oui. J'ai une classe avec laquelle je voudrais travailler avec les deux, un dossier à l'extérieur (au cas où je voudrais changer un paramètre du fichier de configuration) et un JAR qui cache les fichiers de configuration de l'implémentation à l'utilisateur (comme un JAR distribuable à tous).

1 votes

Ainsi, la classe reçoit simplement un PATH vers un fichier (le fichier de configuration).

79voto

Neal Maloney Points 496

C'est délibéré. Le contenu du "fichier" peut ne pas être disponible en tant que fichier. Rappelez-vous que vous avez affaire à des classes et des ressources qui peuvent faire partie d'un fichier JAR ou d'un autre type de ressource. Le classloader n'est pas obligé de fournir un handle de fichier à la ressource, par exemple le fichier JAR peut ne pas avoir été décomposé en fichiers individuels dans le système de fichiers.

Tout ce que vous pouvez faire en obtenant un java.io.File pourrait être fait en copiant le flux dans un fichier temporaire et en faisant de même, si un java.io.File est absolument nécessaire.

6 votes

Vous pouvez ajouter 'rsrc:' lorsque vous appelez votre ressource afin de l'ouvrir, comme par exemple new File("rsrc:nomfichier.txt") qui chargera le nomfichier.txt qui se trouve dans la racine de votre jar.

74voto

Tombart Points 4503

Lorsque vous chargez une ressource, assurez-vous de noter la différence entre :

getClass().getClassLoader().getResource("com/myorg/foo.jpg") //relative path

et

getClass().getResource("/com/myorg/foo.jpg")); //note the slash at the beginning

Je pense que cette confusion est à l'origine de la plupart des problèmes lors du chargement d'une ressource.


De plus, lorsque vous chargez une image, il est plus facile d'utiliser la fonction getResourceAsStream() :

BufferedImage image = ImageIO.read(getClass().getResourceAsStream("/com/myorg/foo.jpg"));

Lorsque vous devez vraiment charger un fichier (non image) à partir d'une archive JAR, vous pouvez essayer ceci :

File file = null;
String resource = "/com/myorg/foo.xml";
URL res = getClass().getResource(resource);
if (res.getProtocol().equals("jar")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile("tempfile", ".tmp");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}

if (file != null && !file.exists()) {
    throw new RuntimeException("Error: File " + file + " not found!");
}

3 votes

+1 Ceci a fonctionné pour moi. Assurez-vous que vous placez les fichiers que vous voulez lire dans le fichier bin et aller dans le répertoire de la classe qui se charge dans les ressources avant d'utiliser le chemin `/com/myorg/filename.ext'.

0 votes

+1 Cela fonctionne également pour moi... Je comprends qu'il peut y avoir des risques de sécurité liés à cette approche, donc les propriétaires d'applications doivent en être conscients.

0 votes

Pourriez-vous clarifier cette affirmation "Il est toujours préférable de charger une ressource avec getResourceAsStream()" ? En quoi cela peut-il apporter une solution au problème ?

13voto

DeProgrammer Points 89

J'ai passé un certain temps à me pencher sur ce problème, car aucune solution que j'ai trouvée ne fonctionnait, bizarrement ! Le répertoire de travail n'est souvent pas le répertoire du JAR, surtout si un JAR (ou tout autre programme, d'ailleurs) est exécuté à partir du menu Démarrer sous Windows. Voici donc ce que j'ai fait, et cela fonctionne pour les fichiers .class exécutés depuis l'extérieur d'un JAR aussi bien que pour un JAR. (Je ne l'ai testé que sous Windows 7).

try {
    //Attempt to get the path of the actual JAR file, because the working directory is frequently not where the file is.
    //Example: file:/D:/all/Java/TitanWaterworks/TitanWaterworks-en.jar!/TitanWaterworks.class
    //Another example: /D:/all/Java/TitanWaterworks/TitanWaterworks.class
    PROGRAM_DIRECTORY = getClass().getClassLoader().getResource("TitanWaterworks.class").getPath(); // Gets the path of the class or jar.

    //Find the last ! and cut it off at that location. If this isn't being run from a jar, there is no !, so it'll cause an exception, which is fine.
    try {
        PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('!'));
    } catch (Exception e) { }

    //Find the last / and cut it off at that location.
    PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('/') + 1);
    //If it starts with /, cut it off.
    if (PROGRAM_DIRECTORY.startsWith("/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(1, PROGRAM_DIRECTORY.length());
    //If it starts with file:/, cut that off, too.
    if (PROGRAM_DIRECTORY.startsWith("file:/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(6, PROGRAM_DIRECTORY.length());
} catch (Exception e) {
    PROGRAM_DIRECTORY = ""; //Current working directory instead.
}

8voto

cd1 Points 2661

Si netclient.p est à l'intérieur d'un fichier JAR, il n'aura pas de chemin d'accès parce que ce fichier est situé à l'intérieur d'un autre fichier. dans ce cas, le meilleur chemin d'accès que vous pouvez avoir est vraiment file:/path/to/jarfile/bot.jar!/config/netclient.p .

0 votes

Lorsque j'essaie de convertir une URL de ce format (...bot.jar!/config/...) en URI, le système me dit que le chemin n'est pas hiérarchique.

7voto

eric manley Points 61

Vous devez comprendre le chemin dans le fichier jar.
Il suffit de le considérer comme relatif. Ainsi, si vous avez un fichier (monfichier.txt), situé dans foo.jar, sous le répertoire \src\main\resources (style maven). Vous y feriez référence comme :

src/main/resources/myfile.txt

Si vous videz votre bocal en utilisant jar -tvf myjar.jar vous verrez la sortie et le chemin relatif à l'intérieur du fichier jar, et vous utiliserez les FORWARD SLASHES.

0 votes

Vous devez en effet utiliser des barres obliques, même sous Windows. Cela implique que vous ne pouvez pas utiliser File.separator .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X