web-dev-qa-db-fra.com

Comment obtenir le chemin d'un fichier JAR en cours d'exécution?

Mon code s'exécute dans un fichier JAR, par exemple, foo.jar, et j'ai besoin de savoir, dans le code, dans quel dossier se trouve le fichier foo.jar en cours d'exécution.

Donc, si foo.jar est dans C:\FOO\, je veux obtenir ce chemin, quel que soit mon répertoire de travail actuel.

549
Thiago Chaves
return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation()
    .toURI()).getPath();

Remplacez "MyClass" par le nom de votre classe.

Évidemment, cela fera des choses étranges si votre classe a été chargée depuis un emplacement autre que le fichier.

512
Zarkonnen

La meilleure solution pour moi:

String path = Test.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = URLDecoder.decode(path, "UTF-8");

Cela devrait résoudre le problème avec des espaces et des caractères spéciaux.

186
Fab

Pour obtenir le File pour un Class donné, il y a deux étapes:

  1. Convertir le Class en un URL
  2. Convertir le URL en un File

Il est important de comprendre les deux étapes et de ne pas les mélanger.

Une fois que vous avez le File, vous pouvez appeler getParentFile pour obtenir le dossier contenant, si c'est ce dont vous avez besoin.

Étape 1: Class à URL

Comme indiqué dans d'autres réponses, il existe deux méthodes principales pour trouver une URL pertinente à une Class.

  1. URL url = Bar.class.getProtectionDomain().getCodeSource().getLocation();

  2. URL url = Bar.class.getResource(Bar.class.getSimpleName() + ".class");

Les deux ont des avantages et des inconvénients.

L’approche getProtectionDomain donne l’emplacement de base de la classe (par exemple, le fichier JAR contenant). Cependant, il est possible que la politique de sécurité du runtime Java jette SecurityException lors de l'appel de getProtectionDomain(). Par conséquent, si votre application doit s'exécuter dans plusieurs environnements, il est préférable de les tester dans chacun d'entre eux. .

L'approche getResource donne le chemin de ressource d'URL complet de la classe, à partir duquel vous devrez effectuer une manipulation de chaîne supplémentaire. Il peut s'agir d'un chemin file:, mais il peut également s'agir de jar:file: ou même de quelque chose de plus méchant comme bundleresource://346.fwk2106232034:4/foo/Bar.class lors de l'exécution dans un cadre OSGi. Inversement, l'approche getProtectionDomain génère correctement une URL file: même à partir de OSGi.

Notez que getResource("") et getResource(".") ont échoué lors de mes tests, lorsque la classe résidait dans un fichier JAR. les deux invocations ont renvoyé null. Je recommande donc l’invocation n ° 2 ci-dessus, car cela semble plus sûr.

Étape 2: URL à File

Quoi qu'il en soit, une fois que vous avez un URL, l'étape suivante consiste à convertir en un File. C'est son propre défi; voir le blog de Kohsuke Kawaguchi à ce sujet pour plus de détails, mais en bref, vous pouvez utiliser new File(url.toURI()) tant que l'URL est complètement formée.

Enfin, je découragerais fortement d'utiliser URLDecoder. Certains caractères de l'URL, : et / en particulier, ne sont pas des caractères valides codés par URL. Depuis le RLDecoder Javadoc:

Il est supposé que tous les caractères de la chaîne codée sont l’un des suivants: "a" à "z", "A" à "Z", "0" à "9" et "-", "_", " .", et "*". Le caractère "%" est autorisé mais est interprété comme le début d'une séquence d'échappement spéciale.

...

Ce décodeur peut traiter les chaînes illégales de deux manières. Cela peut laisser les caractères illégaux ou lancer une exception IllegalArgumentException. L'approche prise par le décodeur est laissée à l'implémentation.

En pratique, URLDecoder ne jette généralement pas IllegalArgumentException comme menacé ci-dessus. Et si votre chemin de fichier contient des espaces codés comme %20, cette approche peut sembler fonctionner. Toutefois, si votre chemin de fichier comporte d'autres caractères non alphanétiques, tels que +, vous aurez des problèmes avec URLDecoder en modifiant votre chemin.

Code de travail

Pour réaliser ces étapes, vous pouvez utiliser des méthodes telles que:

/**
 * Gets the base location of the given class.
 * <p>
 * If the class is directly on the file system (e.g.,
 * "/path/to/my/package/MyClass.class") then it will return the base directory
 * (e.g., "file:/path/to").
 * </p>
 * <p>
 * If the class is within a JAR file (e.g.,
 * "/path/to/my-jar.jar!/my/package/MyClass.class") then it will return the
 * path to the JAR (e.g., "file:/path/to/my-jar.jar").
 * </p>
 *
 * @param c The class whose location is desired.
 * @see FileUtils#urlToFile(URL) to convert the result to a {@link File}.
 */
public static URL getLocation(final Class<?> c) {
    if (c == null) return null; // could not load the class

    // try the easy way first
    try {
        final URL codeSourceLocation =
            c.getProtectionDomain().getCodeSource().getLocation();
        if (codeSourceLocation != null) return codeSourceLocation;
    }
    catch (final SecurityException e) {
        // NB: Cannot access protection domain.
    }
    catch (final NullPointerException e) {
        // NB: Protection domain or code source is null.
    }

    // NB: The easy way failed, so we try the hard way. We ask for the class
    // itself as a resource, then strip the class's path from the URL string,
    // leaving the base path.

    // get the class's raw resource path
    final URL classResource = c.getResource(c.getSimpleName() + ".class");
    if (classResource == null) return null; // cannot find class resource

    final String url = classResource.toString();
    final String suffix = c.getCanonicalName().replace('.', '/') + ".class";
    if (!url.endsWith(suffix)) return null; // weird URL

    // strip the class's path from the URL string
    final String base = url.substring(0, url.length() - suffix.length());

    String path = base;

    // remove the "jar:" prefix and "!/" suffix, if present
    if (path.startsWith("jar:")) path = path.substring(4, path.length() - 2);

    try {
        return new URL(path);
    }
    catch (final MalformedURLException e) {
        e.printStackTrace();
        return null;
    }
} 

/**
 * Converts the given {@link URL} to its corresponding {@link File}.
 * <p>
 * This method is similar to calling {@code new File(url.toURI())} except that
 * it also handles "jar:file:" URLs, returning the path to the JAR file.
 * </p>
 * 
 * @param url The URL to convert.
 * @return A file path suitable for use with e.g. {@link FileInputStream}
 * @throws IllegalArgumentException if the URL does not correspond to a file.
 */
public static File urlToFile(final URL url) {
    return url == null ? null : urlToFile(url.toString());
}

/**
 * Converts the given URL string to its corresponding {@link File}.
 * 
 * @param url The URL to convert.
 * @return A file path suitable for use with e.g. {@link FileInputStream}
 * @throws IllegalArgumentException if the URL does not correspond to a file.
 */
public static File urlToFile(final String url) {
    String path = url;
    if (path.startsWith("jar:")) {
        // remove "jar:" prefix and "!/" suffix
        final int index = path.indexOf("!/");
        path = path.substring(4, index);
    }
    try {
        if (PlatformUtils.isWindows() && path.matches("file:[A-Za-z]:.*")) {
            path = "file:/" + path.substring(5);
        }
        return new File(new URL(path).toURI());
    }
    catch (final MalformedURLException e) {
        // NB: URL is not completely well-formed.
    }
    catch (final URISyntaxException e) {
        // NB: URL is not completely well-formed.
    }
    if (path.startsWith("file:")) {
        // pass through the URL as-is, minus "file:" prefix
        path = path.substring(5);
        return new File(path);
    }
    throw new IllegalArgumentException("Invalid URL: " + url);
}

Vous pouvez trouver ces méthodes dans la bibliothèque SciJava Common :

144
ctrueden

Vous pouvez aussi utiliser:

CodeSource codeSource = YourMainClass.class.getProtectionDomain().getCodeSource();
File jarFile = new File(codeSource.getLocation().toURI().getPath());
String jarDir = jarFile.getParentFile().getPath();
50
Benny Neugebauer

Utilisez ClassLoader.getResource () pour rechercher l'URL de votre classe actuelle.

Par exemple:

package foo;

public class Test
{
    public static void main(String[] args)
    {
        ClassLoader loader = Test.class.getClassLoader();
        System.out.println(loader.getResource("foo/Test.class"));
    }
}

(Cet exemple est tiré de ne question similaire .)

Pour trouver le répertoire, vous devez ensuite séparer l'URL manuellement. Consultez le tutoriel JarClassLoader pour connaître le format d'une URL jar.

24
Jon Skeet

Je suis surpris de voir qu’aucun d’entre eux n’a récemment proposé d’utiliser Path . Voici une citation: " La classe Path comprend diverses méthodes permettant d’obtenir des informations sur le chemin, d’accéder aux éléments du chemin, de convertir le chemin en autres formulaires ou d’extraire des parties d’un fichier. chemin "

Ainsi, une bonne alternative consiste à obtenir l'object Path sous la forme:

Path path = Paths.get(Test.class.getProtectionDomain().getCodeSource().getLocation().toURI());
18
mat_boy

La seule solution qui fonctionne pour moi sous Linux, Mac et Windows:

public static String getJarContainingFolder(Class aclass) throws Exception {
  CodeSource codeSource = aclass.getProtectionDomain().getCodeSource();

  File jarFile;

  if (codeSource.getLocation() != null) {
    jarFile = new File(codeSource.getLocation().toURI());
  }
  else {
    String path = aclass.getResource(aclass.getSimpleName() + ".class").getPath();
    String jarFilePath = path.substring(path.indexOf(":") + 1, path.indexOf("!"));
    jarFilePath = URLDecoder.decode(jarFilePath, "UTF-8");
    jarFile = new File(jarFilePath);
  }
  return jarFile.getParentFile().getAbsolutePath();
}
14
Dmitry Trofimov

Voici la mise à niveau vers d’autres commentaires, qui me paraissent incomplets pour les spécificités de

en utilisant un "dossier" relatif en dehors du fichier .jar (au même emplacement du jar):

String path = 
  YourMainClassName.class.getProtectionDomain().
  getCodeSource().getLocation().getPath();

path = 
  URLDecoder.decode(
    path, 
    "UTF-8");

BufferedImage img = 
  ImageIO.read(
    new File((
        new File(path).getParentFile().getPath()) +  
        File.separator + 
        "folder" + 
        File.separator + 
        "yourfile.jpg"));
8
Zon

J'ai eu le même problème et je l'ai résolu ainsi:

File currentJavaJarFile = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().getPath());   
String currentJavaJarFilePath = currentJavaJarFile.getAbsolutePath();
String currentRootDirectoryPath = currentJavaJarFilePath.replace(currentJavaJarFile.getName(), "");

J'espère que je vous ai aidé.

8
Charlie

Pour obtenir le chemin du fichier jar en cours d’exécution, j’ai étudié les solutions ci-dessus et essayé toutes les méthodes qui diffèrent les unes des autres. Si ces codes sont exécutés dans Eclipse IDE, ils doivent tous être en mesure de trouver le chemin du fichier, y compris la classe indiquée, et d'ouvrir ou de créer un fichier avec le chemin trouvé.

Mais c’est délicat, lorsqu’il est exécuté directement ou par l’intermédiaire de la ligne de commande, lorsqu’il exécute le fichier jar exécutable, le chemin du fichier jar obtenu à partir des méthodes ci-dessus donnera un chemin interne au fichier jar, c’est-à-dire qu’il donne toujours un chemin. comme

rsrc: nom-projet (peut-être devrais-je dire que c'est le nom du paquet du fichier de classe principal - la classe indiquée)

Je ne peux pas convertir le chemin rsrc: ... en un chemin externe, c’est-à-dire lorsqu’il exécute le fichier jar en dehors de Eclipse IDE, il ne peut pas obtenir le chemin du fichier jar.

Le seul moyen possible d’obtenir le chemin d’exécution du fichier jar en dehors d’Eclipse IDE est

System.getProperty("Java.class.path")

cette ligne de code peut renvoyer le chemin d'accès vivant (y compris le nom du fichier) du fichier jar en cours d'exécution (notez que le chemin de retour n'est pas le répertoire de travail), comme le document Java et que certaines personnes ont dit qu'il renverrait les chemins de tous les fichiers de classe dans le même répertoire, mais comme mes tests dans le même répertoire incluent de nombreux fichiers jar, il ne renvoie que le chemin d’exécution de jar (à propos du problème des chemins multiples, en effet il s’est passé dans Eclipse).

6
phchen2

D'autres réponses semblent pointer vers la source de code qui est l'emplacement du fichier Jar qui n'est pas un répertoire.

Utilisation

return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile();
5
F.O.O

la réponse sélectionnée ci-dessus ne fonctionne pas si vous exécutez votre fichier jar en cliquant dessus depuis l'environnement de bureau Gnome (et non depuis un script ou un terminal).

Au lieu de cela, j’ai trouvé que la solution suivante fonctionne partout:

    try {
        return URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(), "UTF-8");
    } catch (UnsupportedEncodingException e) {
        return "";
    }
4
lviggiani

La solution la plus simple consiste à passer le chemin en tant qu'argument lors de l'exécution du fichier jar.

Vous pouvez automatiser cela avec un script Shell (.bat dans Windows, .sh ailleurs):

Java -jar my-jar.jar .

J'ai utilisé . pour transmettre le répertoire de travail actuel.

UPDATE

Vous voudrez peut-être coller le fichier jar dans un sous-répertoire afin que les utilisateurs ne cliquent pas dessus accidentellement. Votre code doit également vérifier que les arguments de la ligne de commande ont bien été fournis et fournir un bon message d'erreur si les arguments sont manquants.

3
Max Heiber

J'ai dû beaucoup déconner avant de finalement trouver une solution efficace (et courte).
Il est possible que la jarLocation soit fournie avec un préfixe tel que file:\ ou jar:file\, qui peut être supprimé à l'aide de String#substring().

URL jarLocationUrl = MyClass.class.getProtectionDomain().getCodeSource().getLocation();
String jarLocation = new File(jarLocationUrl.toString()).getParent();
3
Jelle

En fait, voici une meilleure version - l'ancienne version échouait si un nom de dossier avait un espace.

  private String getJarFolder() {
    // get name and path
    String name = getClass().getName().replace('.', '/');
    name = getClass().getResource("/" + name + ".class").toString();
    // remove junk
    name = name.substring(0, name.indexOf(".jar"));
    name = name.substring(name.lastIndexOf(':')-1, name.lastIndexOf('/')+1).replace('%', ' ');
    // remove escape characters
    String s = "";
    for (int k=0; k<name.length(); k++) {
      s += name.charAt(k);
      if (name.charAt(k) == ' ') k += 2;
    }
    // replace '/' with system separator char
    return s.replace('/', File.separatorChar);
  }

En ce qui concerne les échecs avec les applets, vous n’auriez de toute façon pas accès aux fichiers locaux. Je ne connais pas grand chose à JWS, mais il pourrait ne pas être possible de télécharger l'application pour gérer les fichiers locaux.?

3
bacup lad
String path = getClass().getResource("").getPath();

Le chemin fait toujours référence à la ressource dans le fichier jar.

2
ZZZ

J'ai essayé d'obtenir le chemin d'accès au pot en utilisant

String folder = MyClassName.class.getProtectionDomain().getCodeSource().getLocation().getPath();

c:\app> Java -jar application.jar

En exécutant l'application jar nommée "application.jar", sous Windows dans le dossier "c:\app", la valeur de la variable String "dossier" était "\ c:\app\application.jar "et j'ai eu des problèmes pour tester l'exactitude du chemin

File test = new File(folder);
if(file.isDirectory() && file.canRead()) { //always false }

J'ai donc essayé de définir "test" comme:

String fold= new File(folder).getParentFile().getPath()
File test = new File(fold);

pour obtenir le chemin d'accès dans un format correct comme "c:\app" au lieu de "\ c:\app\application.jar" et j'ai remarqué que cela fonctionnait.

2
public static String dir() throws URISyntaxException
{
    URI path=Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
    String name= Main.class.getPackage().getName()+".jar";
    String path2 = path.getRawPath();
    path2=path2.substring(1);

    if (path2.contains(".jar"))
    {
        path2=path2.replace(name, "");
    }
    return path2;}

Fonctionne bien sur Windows

2
Denton

Je ne suis pas vraiment sûr des autres, mais dans mon cas, cela ne fonctionnait pas avec un "pot exécutable" et je l'ai obtenu en corrigeant ensemble les codes de phchen2 answer et un autre à partir de ce lien: Comment obtenir le chemin d'un Lancer le fichier JAR? Le code:

               String path=new Java.io.File(Server.class.getProtectionDomain()
                .getCodeSource()
                .getLocation()
                .getPath())
          .getAbsolutePath();
       path=path.substring(0, path.lastIndexOf("."));
       path=path+System.getProperty("Java.class.path");
1
Fahad Alkamli

Ce qui est frustrant, c’est que lorsque vous développez dans Eclipse, MyClass.class.getProtectionDomain().getCodeSource().getLocation() renvoie le répertoire /bin, ce qui est très bien, mais lorsque vous le compilez dans un fichier jar, le chemin contient la partie /myjarname.jar qui donne vous noms de fichiers illégaux.

Pour que le code fonctionne à la fois dans l'ide et une fois qu'il est compilé dans un fichier jar, j'utilise le code suivant:

URL applicationRootPathURL = getClass().getProtectionDomain().getCodeSource().getLocation();
File applicationRootPath = new File(applicationRootPathURL.getPath());
File myFile;
if(applicationRootPath.isDirectory()){
    myFile = new File(applicationRootPath, "filename");
}
else{
    myFile = new File(applicationRootPath.getParentFile(), "filename");
}
1
Alexander

J'ai essayé plusieurs solutions, mais aucune n'a donné de résultats corrects dans le cas (probablement spécial) du fait que le fichier jar exécutable a été exporté avec "Packaging external libraries" dans Eclipse. Pour une raison quelconque, toutes les solutions basées sur ProtectionDomain donnent la valeur null dans ce cas.

En combinant certaines des solutions ci-dessus, j'ai réussi à obtenir le code de travail suivant:

String surroundingJar = null;

// gets the path to the jar file if it exists; or the "bin" directory if calling from Eclipse
String jarDir = new File(ClassLoader.getSystemClassLoader().getResource(".").getPath()).getAbsolutePath();

// gets the "bin" directory if calling from Eclipse or the name of the .jar file alone (without its path)
String jarFileFromSys = System.getProperty("Java.class.path").split(";")[0];

// If both are equal that means it is running from an IDE like Eclipse
if (jarFileFromSys.equals(jarDir))
{
    System.out.println("RUNNING FROM IDE!");
    // The path to the jar is the "bin" directory in that case because there is no actual .jar file.
    surroundingJar = jarDir;
}
else
{
    // Combining the path and the name of the .jar file to achieve the final result
    surroundingJar = jarDir + jarFileFromSys.substring(1);
}

System.out.println("JAR File: " + surroundingJar);
1
DragonGamer

Cette méthode, appelée à partir du code de l’archive, renvoie le dossier dans lequel se trouve le fichier .jar. Cela devrait fonctionner sous Windows ou Unix.


  private String getJarFolder() {
    String name = this.getClass().getName().replace('.', '/');
    String s = this.getClass().getResource("/" + name + ".class").toString();
    s = s.replace('/', File.separatorChar);
    s = s.substring(0, s.indexOf(".jar")+4);
    s = s.substring(s.lastIndexOf(':')-1);
    return s.substring(0, s.lastIndexOf(File.separatorChar)+1);
  } 

Dérivé du code à: Déterminer si l'exécution à partir du fichier JAR

0
Bacup Lad

Essaye ça:

String path = new File("").getAbsolutePath();
0
Blarzek

Mentionnez qu'il est vérifié seulement dans Windows mais je pense que cela fonctionne parfaitement sur d'autres systèmes d'exploitation [Linux,MacOs,Solaris]:).


J'ai eu 2.jar fichiers dans le même répertoire. Je voulais que le fichier .jar démarre l'autre fichier .jar qui se trouve dans le même répertoire.

Le problème est que lorsque vous le démarrez à partir de cmd, le répertoire actuel est system32.


Avertissements!

  • Le texte ci-dessous semble bien fonctionner dans tous les tests que j'ai effectués même avec le nom de dossier ;][[;'57f2g34g87-8+9-09!2#@!$%^^&() ou ()%&$%^@# cela fonctionne bien.
  • J'utilise la ProcessBuilder avec comme suit:

???? ..

//The class from which i called this was the class `Main`
String path = getBasePathForClass(Main.class);
String applicationPath=  new File(path + "application.jar").getAbsolutePath();


System.out.println("Directory Path is : "+applicationPath);

//Your know try catch here
//Mention that sometimes it doesn't work for example with folder `;][[;'57f2g34g87-8+9-09!2#@!$%^^&()` 
ProcessBuilder builder = new ProcessBuilder("Java", "-jar", applicationPath);
builder.redirectErrorStream(true);
Process process = builder.start();

//...code

???? getBasePathForClass(Class<?> classs):

    /**
     * Returns the absolute path of the current directory in which the given
     * class
     * file is.
     * 
     * @param classs
     * @return The absolute path of the current directory in which the class
     *         file is.
     * @author GOXR3PLUS[StackOverFlow user] + bachden [StackOverFlow user]
     */
    public static final String getBasePathForClass(Class<?> classs) {

        // Local variables
        File file;
        String basePath = "";
        boolean failed = false;

        // Let's give a first try
        try {
            file = new File(classs.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());

            if (file.isFile() || file.getPath().endsWith(".jar") || file.getPath().endsWith(".Zip")) {
                basePath = file.getParent();
            } else {
                basePath = file.getPath();
            }
        } catch (URISyntaxException ex) {
            failed = true;
            Logger.getLogger(classs.getName()).log(Level.WARNING,
                    "Cannot firgue out base path for class with way (1): ", ex);
        }

        // The above failed?
        if (failed) {
            try {
                file = new File(classs.getClassLoader().getResource("").toURI().getPath());
                basePath = file.getAbsolutePath();

                // the below is for testing purposes...
                // starts with File.separator?
                // String l = local.replaceFirst("[" + File.separator +
                // "/\\\\]", "")
            } catch (URISyntaxException ex) {
                Logger.getLogger(classs.getName()).log(Level.WARNING,
                        "Cannot firgue out base path for class with way (2): ", ex);
            }
        }

        // fix to run inside Eclipse
        if (basePath.endsWith(File.separator + "lib") || basePath.endsWith(File.separator + "bin")
                || basePath.endsWith("bin" + File.separator) || basePath.endsWith("lib" + File.separator)) {
            basePath = basePath.substring(0, basePath.length() - 4);
        }
        // fix to run inside netbeans
        if (basePath.endsWith(File.separator + "build" + File.separator + "classes")) {
            basePath = basePath.substring(0, basePath.length() - 14);
        }
        // end fix
        if (!basePath.endsWith(File.separator)) {
            basePath = basePath + File.separator;
        }
        return basePath;
    }
0
GOXR3PLUS

Ce code a fonctionné pour moi:

private static String getJarPath() throws IOException, URISyntaxException {
    File f = new File(LicensingApp.class.getProtectionDomain().().getLocation().toURI());
    String jarPath = f.getCanonicalPath().toString();
    String jarDir = jarPath.substring( 0, jarPath.lastIndexOf( File.separator ));
    return jarDir;
  }
0
John Lockwood