Wie erhalte ich den Pfad einer laufenden JAR-Datei?


580

Mein Code wird in einer JAR-Datei ausgeführt, z. B. foo.jar , und ich muss im Code wissen, in welchem ​​Ordner sich foo.jar befindet.

Wenn also foo.jar vorhanden ist C:\FOO\, möchte ich diesen Pfad unabhängig von meinem aktuellen Arbeitsverzeichnis abrufen.


In Fabs Antwort finden Sie eine Lösung, die funktioniert, wenn Pfade Leerzeichen enthalten. Beachten Sie außerdem, dass einige der folgenden Antworten die Frage im Titel (JAR-Pfad), andere die Frage selbst (Pfad des Ordners, der JAR enthält) und einige Pfade zu Klassen in der JAR-Datei enthalten.
Andy Thomas

32
Vorsicht bei der Verwendung in ANT! ============== Ich rufe String path = SomeClass.class.getProtectionDomain (). GetCodeSource (). GetLocation (). GetPath () auf; und erhalten: /C:/apache-ant-1.7.1/lib/ant.jar Nicht sehr nützlich!
Dino Fancellu

Interessant. Der ursprüngliche Code, in dem ich dies verwendet habe, wurde nie in ant ausgeführt, daher ist dies für mich kein Problem.
Thiago Chaves

2
@Dino Fancellu, ich habe genau das erlebt, was du beschrieben hast. Funktioniert während der Entwicklung, schlägt fehl, wenn es in jar gebaut wird.
Buddy

Antworten:


539
return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation()
    .toURI()).getPath();

Ersetzen Sie "MyClass" durch den Namen Ihrer Klasse.

Offensichtlich wird dies seltsame Dinge bewirken, wenn Ihre Klasse von einem Nicht-Dateispeicherort geladen wurde.


43
Der toURI()Schritt ist wichtig, um Probleme mit Sonderzeichen, einschließlich Leerzeichen und Pluszeichen, zu vermeiden. Der richtige Einzeiler ist: Die return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI()); Verwendung URLDecoderfunktioniert nicht für viele Sonderzeichen. Siehe meine Antwort unten für weitere Details.
ctrueden

4
Hinweis: Dies gibt den Pfad einschließlich des Namens der JAR-Datei zurück
Buddy

8
Zeigt dies nicht auf die JAR-Datei anstelle des laufenden Verzeichnisses? Für diese Arbeit müssen Sie das Ergebnis getParentFile () ausführen.
FOO

1
Auch getProtectionDomainist null, wenn Sie Ihre Klasse von einem stracktrace erhalten:val strace = Thread.currentThread().getStackTrace; val path = strace(1).getClass.getProtectionDomain
bbarker

1
Verwendung dieser Methode mit bis zu Java 8; Wenn Sie diese Methode in eine Klasse einfügen, die sich in einem externen Jar befindet und über den Klassenpfad geladen wird, wird der Pfad des externen Jar anstelle des tatsächlich laufenden Jar angegeben.
Mr00Anderson

189

Beste Lösung für mich:

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

Dies sollte das Problem mit Leerzeichen und Sonderzeichen lösen.


8
Noch ein Hinweis: Beim Aufrufen dieser Funktion aus dem Jar wird der Name des Jars am Ende für mich angehängt, daher musste ausgeführt werden: path.substring (0, path.lastIndexOf ("/") + 1);
Will824

11
Ich bin nicht unbedingt das Pfadtrennzeichen. Sie sollten stattdessen (neue Datei (Pfad)). GetParentFile (). GetPath () ausführen.
pjz

10
Keine Probleme mit dem Anhängen des JAR-Dateinamens. Die UTF-Konvertierung scheint die perfekte Lösung in Kombination mit @Iviggiani irgendjemandes ( URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(), "UTF-8");) unter Linux zu sein. Ich habe Windows jedoch nicht ausprobiert.
Ubuntudroid

2
Vielen Dank, dies ermöglichte mir das Laden von Dateien außerhalb meiner JAR mit FileInputStream unter Linux und Windows. Musste nur den dekodierten Pfad vor dem Dateinamen hinzufügen ...
giorgio79

11
Achtung: Es wird nicht empfohlen, URLDecoderSonderzeichen zu dekodieren. Insbesondere werden Zeichen wie +fälschlicherweise in Leerzeichen dekodiert. Siehe meine Antwort für Details.
ctrueden

153

Um das Filefür eine bestimmte zu erhalten Class, gibt es zwei Schritte:

  1. Konvertieren Sie die Classin aURL
  2. Konvertieren Sie die URLin aFile

Es ist wichtig, beide Schritte zu verstehen und nicht zusammenzuführen.

Sobald Sie die haben File, können Sie anrufengetParentFile , um den enthaltenen Ordner zu erhalten, wenn Sie dies benötigen.

Schritt 1: ClassbisURL

Wie in anderen Antworten erläutert, gibt es zwei Hauptmethoden, um eine URLfür a relevante zu finden Class.

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

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

Beide haben Vor- und Nachteile.

Der getProtectionDomainAnsatz liefert den Basisort der Klasse (z. B. die enthaltende JAR-Datei). Es ist jedoch möglich, dass die Sicherheitsrichtlinie der Java-Laufzeit SecurityExceptionbeim Aufrufen getProtectionDomain()ausgelöst wird. Wenn Ihre Anwendung in einer Vielzahl von Umgebungen ausgeführt werden muss, ist es am besten, sie in allen Umgebungen zu testen.

Der getResourceAnsatz liefert den vollständigen URL-Ressourcenpfad der Klasse, von dem aus Sie zusätzliche Zeichenfolgenmanipulationen durchführen müssen. Es kann ein file:Pfad sein, aber es kann auch jar:file:oder sogar etwas Unangenehmeres sein, bundleresource://346.fwk2106232034:4/foo/Bar.classwenn es innerhalb eines OSGi-Frameworks ausgeführt wird. Umgekehrt getProtectionDomainergibt der Ansatz korrekt afile: auch innerhalb von OSGi URL.

Beachten Sie, dass beide getResource("")und getResource(".")in meinen Tests fehlgeschlagen sind, als sich die Klasse in einer JAR-Datei befand. Beide Aufrufe gaben null zurück. Daher empfehle ich stattdessen den oben gezeigten Aufruf Nr. 2, da er sicherer erscheint.

Schritt 2: URLbisFile

In beiden Fällen ist URLder nächste Schritt , sobald Sie eine haben , die Konvertierung in a File. Dies ist seine eigene Herausforderung; siehe Kohsuke Kawaguchi Blog - Post über sie für weitere Informationen, aber kurz gesagt, können Sienew File(url.toURI()) , solange die URL vollständig wohlgeformt ist .

Schließlich möchte ich sehr entmutigen verwenden URLDecoder. Einige Zeichen der URL :und /insbesondere sind keine gültigen URL-codierten Zeichen. Aus dem URLDecoder Javadoc:

Es wird angenommen, dass alle Zeichen in der codierten Zeichenfolge eines der folgenden sind: "a" bis "z", "A" bis "Z", "0" bis "9" und "-", "_", " .", und "*". Das Zeichen "%" ist zulässig, wird jedoch als Beginn einer speziellen Escape-Sequenz interpretiert.

...

Es gibt zwei Möglichkeiten, wie dieser Decoder mit illegalen Zeichenfolgen umgehen kann. Es könnte entweder illegale Zeichen in Ruhe lassen oder eine IllegalArgumentException auslösen. Welchen Ansatz der Decoder verfolgt, bleibt der Implementierung überlassen.

In der Praxis wird im URLDecoderAllgemeinen nicht IllegalArgumentExceptionwie oben bedroht geworfen . Wenn in Ihrem Dateipfad Leerzeichen codiert sind %20, scheint dieser Ansatz zu funktionieren. Wenn Ihr Dateipfad jedoch andere nicht alphabetische Zeichen enthält, wie z. B. +Probleme beim URLDecoderVerwischen Ihres Dateipfads.

Arbeitscode

Um diese Schritte auszuführen, haben Sie möglicherweise folgende Methoden:

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

Sie finden diese Methoden in der SciJava Common- Bibliothek:


5
+1; Die bisher beste Antwort: Der Pfad wird in der richtigen Notation für das Betriebssystem zurückgegeben. (zB \ für Windows).
Bathseba

In Bezug auf die Sicherheit habe ich festgestellt, dass Java WebStart dies nicht zugelassen hat.
Thorbjørn Ravn Andersen

55

Sie können auch verwenden:

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

3
Das funktioniert besser für mich, weil es den Weg des Glases gibt, nicht der Klasse!
T30

Hat auch für mich gearbeitet. Kombiniere es mit Fabs Antwort und es wird besser!
Danielson Alves Júnior

25

Verwenden Sie ClassLoader.getResource (), um die URL für Ihre aktuelle Klasse zu finden.

Zum Beispiel:

package foo;

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

(Dieses Beispiel stammt aus einer ähnlichen Frage .)

Um das Verzeichnis zu finden, müssen Sie die URL manuell zerlegen. Informationen zum Format einer JAR-URL finden Sie im JarClassLoader-Tutorial .


Meine JAR-Datei ist verschleiert, daher löst diese Antwort mein Problem nicht. Aber ich habe das in der Frage nicht angegeben, daher ist dies immer noch eine gültige Antwort.
Thiago Chaves

12
Wenn es verschleiert ist, verwenden Sie Test.class.getName () und führen Sie das entsprechende Munging durch.
Jon Skeet

1
@ JonSkeet Es gibt so viele Probleme mit Ihrer Antwort: 1. Es wird keine geben, NPEweil Sie die gestellte Frage nicht beantwortet haben (Pfad zu JAR dir wurde gestellt und Sie haben auf eine völlig andere Frage geantwortet: Pfad zur Klasse). 2. Wie von anderen erwähnt, und ich habe das gleiche Problem, funktioniert es nicht für Applets. 3. Der zurückgegebene Pfad ist überhaupt nicht kanonisch dargestellt : jar:file:/listener/build/libs/listener-1.0.0-all.jar!/shared/Test.class.
WhiteAngel

1
@WhiteAngel: 1) Die letzte Zeile meines Beitrags zeigt an, dass Sie sich die URL ansehen und diese auseinander nehmen müssen, um die JAR-Datei zu erhalten. Ich bin damit einverstanden, dass es nicht die vollständigste Antwort ist, aber ich denke nicht, dass es wirklich so schlimm ist, darüber zu streiten (besonders 10 Jahre später ...). 2) Applets wurden hier in keinem Kommentar erwähnt - seltsamerweise nicht Ich habe keine Zeit, mir alle Kommentare zu allen Antworten auf Fragen anzusehen, auf die ich zufällig eine Antwort gepostet habe. 3) Ich verlinke wieder auf das Format der JAR-URL.
Jon Skeet

2
@ WhiteAngel: Ist es die beste Antwort, die ich je geschrieben habe? Nee. Ist es so schlimm, wie du es dir vorstellst? Nein, das glaube ich nicht. (Insbesondere in Bezug auf die Behauptungen, die Sie aufgestellt haben, um eine NPE zu werfen, was nicht der Fall ist.) Ich würde vorschlagen, dass Sie Ihre eigene Antwort hinzufügen, anstatt sich über diese zu aufregen. Das wäre ein positiverer Ansatz.
Jon Skeet

19

Ich bin überrascht zu sehen, dass keiner kürzlich vorgeschlagen hat, es zu verwenden Path. Hier folgt ein Zitat: " Die PathKlasse enthält verschiedene Methoden, mit denen Informationen über den Pfad abgerufen, auf Elemente des Pfads zugegriffen, der Pfad in andere Formen konvertiert oder Teile eines Pfads extrahiert werden können. "

Eine gute Alternative ist es daher, das PathObjekt wie folgt zu erhalten :

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

3
Als Hinweis ist Path ab Java 7 verfügbar.
Chris Forrence

15

Die einzige Lösung, die für mich unter Linux, Mac und Windows funktioniert:

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

Dies wird nicht funktionieren. Unter Linux löst die toUri () -Methode eine Ausnahme aus, und Sie erreichen den else-Teil für Linux nicht.
Wilhelm Sorban

9

Ich hatte das gleiche Problem und habe es so gelöst:

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

Ich hoffe ich habe dir geholfen.


Tu das nicht. URL.getPath () gibt keinen Dateinamen zurück und schlägt unter vielen Umständen fehl, z. B. bei Dateipfaden mit Leerzeichen.
VGR

9

Hier ist ein Upgrade auf andere Kommentare, die mir für die Einzelheiten von unvollständig erscheinen

Verwenden eines relativen "Ordners" außerhalb der JAR-Datei (am selben Speicherort des 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"));

4
Achtung: Es wird nicht empfohlen, URLDecoderSonderzeichen zu dekodieren. Insbesondere werden Zeichen wie +fälschlicherweise in Leerzeichen dekodiert. Siehe meine Antwort für Details.
ctrueden

Die Verwendung von Sonderzeichen in Dateinamen wird nicht empfohlen.
Zon

URLDecoderdient trotz seines Namens zum Dekodieren von URL- und Formularparameternamen und -werten, nicht von URLs.
Marquis von Lorne

6

Um den Pfad zum Ausführen der JAR-Datei zu ermitteln, habe ich die oben genannten Lösungen untersucht und alle Methoden ausprobiert, bei denen Unterschiede bestehen. Wenn dieser Code in der Eclipse-IDE ausgeführt wird, sollten alle in der Lage sein, den Pfad der Datei einschließlich der angegebenen Klasse zu finden und eine angegebene Datei mit dem gefundenen Pfad zu öffnen oder zu erstellen.

Es ist jedoch schwierig, wenn die ausführbare JAR-Datei direkt oder über die Befehlszeile ausgeführt wird. Dies schlägt fehl, da der Pfad der JAR-Datei, der mit den oben genannten Methoden abgerufen wurde, einen internen Pfad in der JAR-Datei angibt, dh immer einen Pfad angibt wie

rsrc: Projektname (vielleicht sollte ich sagen, dass es der Paketname der Hauptklassendatei ist - die angegebene Klasse)

Ich kann den Pfad rsrc: ... nicht in einen externen Pfad konvertieren. Wenn die JAR-Datei außerhalb der Eclipse-IDE ausgeführt wird, kann der Pfad der JAR-Datei nicht abgerufen werden.

Die einzige Möglichkeit, den Pfad zum Ausführen der JAR-Datei außerhalb der Eclipse-IDE abzurufen, ist

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

Diese Codezeile gibt möglicherweise den lebenden Pfad (einschließlich des Dateinamens) der laufenden JAR-Datei zurück (beachten Sie, dass der Rückgabepfad nicht das Arbeitsverzeichnis ist), da das Java-Dokument und einige Leute sagten, dass die Pfade aller Klassendateien zurückgegeben werden im selben Verzeichnis, aber da meine Tests, wenn sie im selben Verzeichnis viele JAR-Dateien enthalten, nur den Pfad zum Ausführen von JAR zurückgeben (über das Problem mit mehreren Pfaden, das tatsächlich in der Eclipse aufgetreten ist).


java.class.pathkann mehrwertig sein. Einer dieser Werte liefert sicherlich das Verzeichnis oder die JAR-Datei, in der sich die aktuelle Klasse befindet, aber welcher?
Marquis von Lorne

Ich bestätige, ich habe andere Lösungen ausprobiert, aber nie den Dateinamen des Glases erhalten. Das funktioniert ganz einfach! danke - +1
guillaume girod-vitouchkina

5

Andere Antworten scheinen auf die Codequelle zu verweisen, bei der es sich um den Speicherort der Jar-Datei handelt, bei dem es sich nicht um ein Verzeichnis handelt.

Verwenden

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

Dies kann ein Verzeichnis sein, wenn Sie Ihre Klassen aus einem Dateisystem anstelle einer JAR-Datei laden, z. B. beim Debuggen.
Marquis von Lorne

4

Die oben ausgewählte Antwort funktioniert nicht, wenn Sie Ihr JAR ausführen, indem Sie in der Gnome-Desktop-Umgebung darauf klicken (nicht in einem Skript oder Terminal).

Stattdessen finde ich es gut, dass die folgende Lösung überall funktioniert:

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

2
Haben Sie das in einem Applet oder einer App versucht? mit Java Web Start gestartet? Mein Verständnis ist, dass es in beiden Situationen fehlschlagen wird (auch wenn die App vertrauenswürdig ist).
Andrew Thompson

Diese Lösung kann nur den Speicherort "." Zurückgeben. innerhalb der JAR - Datei, nicht die Lage von der JAR - Datei.
Marquis von Lorne

Achtung: Es wird nicht empfohlen, URLDecoderSonderzeichen zu dekodieren. Insbesondere werden Zeichen wie +fälschlicherweise in Leerzeichen dekodiert. Siehe meine Antwort für Details.
ctrueden

Im Frühlingsstiefel wird es werfenNullPointerException
Ravi Parekh

Sie haben, NPEwenn es keine Ressourcen in JAR gibt.
WhiteAngel

3

Eigentlich ist hier eine bessere Version - die alte ist fehlgeschlagen, wenn ein Ordnername ein Leerzeichen enthält.

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

Wenn Sie mit Applets fehlschlagen, haben Sie normalerweise sowieso keinen Zugriff auf lokale Dateien. Ich weiß nicht viel über JWS, aber um lokale Dateien zu verarbeiten, ist es möglicherweise nicht möglich, die App herunterzuladen.


Es gibt verschiedene integrierte Möglichkeiten, den Pfad zu dekodieren. Sie müssen keinen eigenen Code schreiben.
Marquis von Lorne

3

Ich habe versucht, den Glaslaufpfad mit zu bekommen

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

c: \ app> java -jar application.jar

Beim Ausführen der JAR-Anwendung "application.jar" unter Windows im Ordner " c: \ app " lautete der Wert der String-Variablen "folder" " \ c: \ app \ application.jar ", und ich hatte Probleme beim Testen die Richtigkeit des Pfades

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

Also habe ich versucht, "Test" zu definieren als:

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

Pfad in einem richtigen Format wie " c: \ app " anstelle von " \ c: \ app \ application.jar " zu erhalten, und ich bemerkte, dass es funktioniert.


3

Die einfachste Lösung besteht darin, den Pfad als Argument beim Ausführen des JARs zu übergeben.

Sie können dies mit einem Shell-Skript automatisieren (.bat in Windows, .sh an einer anderen Stelle):

java -jar my-jar.jar .

Ich habe .das aktuelle Arbeitsverzeichnis übergeben.

AKTUALISIEREN

Möglicherweise möchten Sie die JAR-Datei in einem Unterverzeichnis speichern, damit Benutzer nicht versehentlich darauf klicken. Ihr Code sollte auch überprüfen, ob die Befehlszeilenargumente angegeben wurden, und eine gute Fehlermeldung anzeigen, wenn die Argumente fehlen.


3

Ich musste viel herumspielen, bevor ich endlich eine funktionierende (und kurze) Lösung fand.
Es ist möglich, dass das jarLocationmit einem Präfix wie file:\oder geliefert wird jar:file\, das mit verwendet werden kann String#substring().

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

2
String path = getClass().getResource("").getPath();

Der Pfad bezieht sich immer auf die Ressource in der JAR-Datei.


1
Diese Pfadzeichenfolge muss noch an Ihre Bedürfnisse angepasst werden. String path = new File(getClass().getResource("").getPath()).getParentFile().getParent(); File jarDir = new File(path.substring(5));
ZZZ

4
Beides getResource("")und getResource(".")fehlgeschlagen in meinen Tests, als sich die Klasse in einer JAR-Datei befand; Beide Aufrufe gaben null zurück.
ctrueden

2
Das wirft NullPointerException.
Marquis von Lorne

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

Funktioniert gut unter Windows


1

Etwas , das frustrierend ist, dass , wenn Sie in Eclipse entwickeln , MyClass.class.getProtectionDomain().getCodeSource().getLocation()das zurückgibt /binVerzeichnis , das ist sehr gut, aber wenn man es in ein Gefäß zu kompilieren, enthält der Pfad der/myjarname.jar Teil, der Ihnen illegale Dateinamen gibt.

Damit der Code sowohl in der ide als auch nach dem Kompilieren in ein Glas funktioniert, verwende ich den folgenden Code:

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

Ich bin mir bei den anderen nicht wirklich sicher, aber in meinem Fall funktionierte es nicht mit einem "Runnable JAR" und ich habe es zum Laufen gebracht, indem ich Codes aus der Antwort von phchen2 und einem anderen über diesen Link zusammengesetzt habe: Wie erhalte ich den Pfad einer laufenden JAR-Datei? Der 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

Ich habe dort oben mehrere Lösungen ausprobiert, aber keine lieferte korrekte Ergebnisse für den (wahrscheinlich speziellen) Fall, dass das ausführbare JAR mit "Externe Bibliotheken verpacken" in Eclipse exportiert wurde. Aus irgendeinem Grund führen alle auf der ProtectionDomain basierenden Lösungen in diesem Fall zu null.

Durch die Kombination einiger der oben genannten Lösungen gelang es mir, den folgenden Arbeitscode zu erreichen:

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

Versuche dies:

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

0

Diese Methode, die aus dem Code im Archiv aufgerufen wird, gibt den Ordner zurück, in dem sich die JAR-Datei befindet. Es sollte entweder unter Windows oder unter Unix funktionieren.


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

Abgeleitet vom Code unter: Bestimmen Sie, ob JAR ausgeführt wird


3
"Es sollte entweder unter Windows oder unter Unix funktionieren." wird aber in jedem Applet und jeder App fehlschlagen. mit JWS gestartet.
Andrew Thompson

0

Erwähnen Sie, dass es nur eingecheckt ist, Windowsaber ich denke, es funktioniert perfekt auf anderen Betriebssystemen [ Linux,MacOs,Solaris] :).


Ich hatte 2 .jar Dateien im selben Verzeichnis. Ich wollte von der einen .jarDatei die andere .jarDatei starten, die sich im selben Verzeichnis befindet.

Das Problem ist, dass beim Starten aus cmddem aktuellen Verzeichnis ist system32.


Warnungen!

  • Das Folgende scheint in all den Tests, die ich selbst mit Ordnernamen ;][[;'57f2g34g87-8+9-09!2#@!$%^^&()oder durchgeführt habe, ziemlich gut zu funktionieren()%&$%^@# es funktioniert gut.
  • Ich benutze das ProcessBuildermit dem folgenden wie folgt:

🍂 ..

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

Dieser Code hat bei mir funktioniert:

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

Mit diesem Code konnte ich feststellen, ob das Programm in einer JAR-Datei oder IDE ausgeführt wird:

private static boolean isRunningOverJar() {
    try {
        String pathJar = Application.class.getResource(Application.class.getSimpleName() + ".class").getFile();

        if (pathJar.toLowerCase().contains(".jar")) {
            return true;
        } else {
            return false;
        }
    } catch (Exception e) {
        return false;
    }
}

Wenn ich den vollständigen Windows-Pfad der JAR-Datei abrufen muss, verwende ich diese Methode:

    private static String getPathJar() {
        try {
            final URI jarUriPath =
                    Application.class.getResource(Application.class.getSimpleName() + ".class").toURI();
            String jarStringPath = jarUriPath.toString().replace("jar:", "");
            String jarCleanPath  = Paths.get(new URI(jarStringPath)).toString();

            if (jarCleanPath.toLowerCase().contains(".jar")) {
                return jarCleanPath.substring(0, jarCleanPath.lastIndexOf(".jar") + 4);
            } else {
                return null;
            }
        } catch (Exception e) {
            log.error("Error getting JAR path.", e);
            return null;
        }
    }

Mein vollständiger Code, der mit einer Spring Boot-Anwendung unter Verwendung der CommandLineRunnerImplementierung arbeitet, um sicherzustellen, dass die Anwendung immer in einer Konsolenansicht ausgeführt wird (versehentlich doppelte Klicks im JAR-Dateinamen), verwende ich den nächsten Code:

@SpringBootApplication
public class Application implements CommandLineRunner {
    public static void main(String[] args) throws IOException {
        Console console = System.console();

        if (console == null && !GraphicsEnvironment.isHeadless() && isRunningOverJar()) {
            Runtime.getRuntime().exec(new String[]{"cmd", "/c", "start", "cmd", "/k",
                    "java -jar \"" + getPathJar() + "\""});
        } else {
            SpringApplication.run(Application.class, args);
        }
    }

    @Override
    public void run(String... args) {
        /*
        Additional code here...
        */
    }

    private static boolean isRunningOverJar() {
        try {
            String pathJar = Application.class.getResource(Application.class.getSimpleName() + ".class").getFile();

            if (pathJar.toLowerCase().contains(".jar")) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            return false;
        }
    }

    private static String getPathJar() {
        try {
            final URI jarUriPath =
                    Application.class.getResource(Application.class.getSimpleName() + ".class").toURI();
            String jarStringPath = jarUriPath.toString().replace("jar:", "");
            String jarCleanPath  = Paths.get(new URI(jarStringPath)).toString();

            if (jarCleanPath.toLowerCase().contains(".jar")) {
                return jarCleanPath.substring(0, jarCleanPath.lastIndexOf(".jar") + 4);
            } else {
                return null;
            }
        } catch (Exception e) {
            return null;
        }
    }
}

-1

Ich schreibe in Java 7 und teste in Windows 7 mit der Laufzeit von Oracle und Ubuntu mit der Open Source-Laufzeit. Dies funktioniert perfekt für diese Systeme:

Der Pfad für das übergeordnete Verzeichnis einer laufenden JAR-Datei (vorausgesetzt, die Klasse, die diesen Code aufruft, ist ein direktes untergeordnetes Element des JAR-Archivs selbst):

try {
    fooDir = new File(this.getClass().getClassLoader().getResource("").toURI());
} catch (URISyntaxException e) {
    //may be sloppy, but don't really need anything here
}
fooDirPath = fooDir.toString(); // converts abstract (absolute) path to a String

Der Pfad von foo.jar wäre also:

fooPath = fooDirPath + File.separator + "foo.jar";

Auch dies wurde auf keinem Mac oder älteren Windows getestet


-1

Der getProtectionDomainAnsatz funktioniert manchmal nicht, z. B. wenn Sie das JAR für einige der Java-Kernklassen finden müssen (z. B. in meiner Fallklasse StringBuilderinnerhalb von IBM JDK). Das Folgende funktioniert jedoch nahtlos:

public static void main(String[] args) {
    System.out.println(findSource(MyClass.class));
    // OR
    System.out.println(findSource(String.class));
}

public static String findSource(Class<?> clazz) {
    String resourceToSearch = '/' + clazz.getName().replace(".", "/") + ".class";
    java.net.URL location = clazz.getResource(resourceToSearch);
    String sourcePath = location.getPath();
    // Optional, Remove junk
    return sourcePath.replace("file:", "").replace("!" + resourceToSearch, "");
}

URL.getPath () macht nicht das, was Sie denken. Sonderzeichen werden prozentual codiert.
VGR

-1

Ich habe eine andere Möglichkeit, den String-Speicherort einer Klasse zu ermitteln.

URL path = Thread.currentThread().getContextClassLoader().getResource("");
Path p = Paths.get(path.toURI());
String location = p.toString();

Die Ausgabezeichenfolge hat die Form

C:\Users\Administrator\new Workspace\...

Die Leerzeichen und andere Zeichen werden behandelt und in der Form ohne file:/. So wird es einfacher zu bedienen sein.

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.