Jak uzyskać ścieżkę do działającego pliku JAR?

580

Mój kod działa w pliku JAR, powiedzmy foo.jar , i muszę wiedzieć w kodzie, w którym folderze jest uruchomiony plik foo.jar .

Tak więc, jeśli jest uruchomiony plik foo.jarC:\FOO\ , chcę uzyskać tę ścieżkę bez względu na mój bieżący katalog roboczy.

Thiago Chaves
źródło
Zobacz odpowiedź Fab na rozwiązanie, które działa, gdy ścieżki zawierają spacje. Zauważ też, że niektóre odpowiedzi poniżej odnoszą się do pytania w tytule (ścieżka do jar), niektóre do samego pytania (ścieżka do folderu zawierającego jar), a niektóre zawierają ścieżki do klas w pliku jar.
Andy Thomas,
32
Uważaj podczas korzystania z ANT! ============== Wywołuję String path = SomeClass.class.getProtectionDomain (). GetCodeSource (). GetLocation (). GetPath (); i pobierz: /C:/apache-ant-1.7.1/lib/ant.jar Niezbyt przydatne!
Dino Fancellu
Ciekawy. Oryginalny kod, w którym go użyłem, nigdy nie był uruchamiany w Ant, więc nie stanowi to dla mnie problemu.
Thiago Chaves
2
@Dino Fancellu, doświadczyłem dokładnie tego, co opisałeś. Działa podczas tworzenia, nie działa po zbudowaniu do jar.
Buddy,

Odpowiedzi:

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

Zamień „MyClass” na nazwę swojej klasy.

Oczywiście zrobi to dziwne rzeczy, jeśli klasa została załadowana z lokalizacji innej niż plik.

Zarkonnen
źródło
42
Ten toURI()krok jest niezbędny, aby uniknąć problemów ze znakami specjalnymi, w tym spacjami i plusami. Prawidłowy jednowierszowy to: return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI()); Używanie URLDecodernie działa w przypadku wielu znaków specjalnych. Zobacz moją odpowiedź poniżej, aby uzyskać więcej informacji.
ctrueden
4
Uwaga: zwraca ścieżkę zawierającą nazwę pliku jar
Buddy
8
Czy nie wskazuje to na plik jar zamiast uruchomionego katalogu? Będziesz musiał wykonać wynik getParentFile () dla tej pracy.
FOO,
1
Również getProtectionDomainjest zerowy, jeśli pobierasz klasę z trasy:val strace = Thread.currentThread().getStackTrace; val path = strace(1).getClass.getProtectionDomain
bbarker
1
Korzystanie z tej metody w wersji do Java 8; umieszczając tę ​​metodę w klasie znajdującej się w zewnętrznym słoiku, ładowanym przez ścieżkę klasy, wówczas zamiast faktycznego działającego słoika zostanie podana ścieżka zewnętrznego słoika.
Mr00Anderson
189

Najlepsze rozwiązanie dla mnie:

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

To powinno rozwiązać problem ze spacjami i znakami specjalnymi.

Fab
źródło
8
Jeszcze jedna uwaga: podczas wywoływania tej funkcji z Jar, na końcu dołączana jest nazwa jar, dlatego musiałem wykonać: path.substring (0, path.lastIndexOf ("/") + 1);
will824,
11
/ niekoniecznie jest separatorem ścieżek. Powinieneś zrobić (nowy plik (ścieżka)). GetParentFile (). GetPath () zamiast tego.
pjz
10
Nie ma problemów z dodawaniem nazwy pliku JAR. Konwersja UTF wydaje się być idealnym rozwiązaniem w połączeniu z @Iviggiani one ( URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(), "UTF-8");) w systemie Linux. Nie próbowałem jednak w systemie Windows.
ubuntudroid
2
Dziękuję, to pozwoliło mi załadować pliki zewnętrzne do mojego pliku JAR za pomocą FileInputStream zarówno w systemie Linux, jak i Windows. Musiałem tylko dodać zdekodowaną ścieżkę przed nazwą pliku ...
giorgio79
11
Uwaga: nie zaleca się używania URLDecoderdo dekodowania znaków specjalnych. W szczególności takie znaki +zostaną błędnie zdekodowane na spacje. Zobacz moją odpowiedź, aby poznać szczegóły.
ctrueden
153

Aby uzyskać Filedla danego Class, istnieją dwa kroki:

  1. Przekonwertować Classdo AURL
  2. Przekonwertować URLdo AFile

Ważne jest, aby zrozumieć oba kroki i nie łączyć ich.

Gdy już to Filezrobisz, możesz zadzwonićgetParentFile aby uzyskać folder zawierający, jeśli tego potrzebujesz.

Krok 1: ClassdoURL

Jak omówiono w innych odpowiedziach, istnieją dwa główne sposoby znalezienia związku URLz Class.

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

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

Oba mają zalety i wady.

getProtectionDomainPodejście daje położenie podstawy klasy (na przykład zawierający plik JAR). Możliwe jest jednak, że zasady bezpieczeństwa środowiska wykonawczego Java zostaną wyrzucone SecurityExceptionpodczas wywoływaniagetProtectionDomain() , więc jeśli aplikacja musi działać w różnych środowiskach, najlepiej przetestować je we wszystkich.

getResourcePodejście daje pełną ścieżkę URL zasobu klasy, z której trzeba będzie wykonać dodatkowe łańcuchach. Może to być file:ścieżka, ale może to być jar:file:lub nawet coś paskudniejszego bundleresource://346.fwk2106232034:4/foo/Bar.classpodczas wykonywania w środowisku OSGi. I odwrotnie, getProtectionDomainpodejście poprawnie daje afile: adres URL nawet z poziomu OSGi.

Zauważ, że jedno getResource("")i drugie getResource(".")nie powiodło się w moich testach, gdy klasa rezydowała w pliku JAR; oba wywołania zwróciły wartość null. Polecam więc wywołanie nr 2 pokazane powyżej, ponieważ wydaje się bezpieczniejsze.

Krok 2: URLdoFile

Tak czy inaczej, kiedy już masz URL, następnym krokiem jest konwersja na File. To jest własne wyzwanie; zobacz post na blogu Kohsuke Kawaguchi, aby uzyskać szczegółowe informacje, ale w skrócie możesz użyć, new File(url.toURI())o ile adres URL jest całkowicie dobrze sformułowany.

Na koniec odradzałbym używanie URLDecoder. Niektórzy bohaterowie zawartości, :a /w szczególności nie może zawierać znaków URL-zakodowane. Z URLDecoder Javadoc:

Zakłada się, że wszystkie znaki w zakodowanym ciągu są jednym z następujących: „a” do „z”, „A” do „Z”, „0” do „9” oraz „-”, „_”, „ .", i "*". Znak „%” jest dozwolony, ale jest interpretowany jako początek specjalnej sekwencji ucieczki.

...

Istnieją dwa możliwe sposoby, w jakie ten dekoder radzi sobie z nielegalnymi łańcuchami. Może pozostawić nielegalne znaki w spokoju lub zgłosić wyjątek IllegalArgumentException. To, jakie podejście przyjmuje dekoder, należy do implementacji.

W praktyce na URLDecoderogół nie rzuca się IllegalArgumentExceptiontak, jak grozi powyżej. A jeśli ścieżka do pliku ma spacje zakodowane jako %20, to podejście może wydawać się działać. Jeśli jednak ścieżka do pliku zawiera inne znaki niealfameryczne, takie jak +problemy z URLDecodermanipulowaniem ścieżką.

Działający kod

Aby wykonać te kroki, możesz mieć metody takie jak następujące:

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

Te metody można znaleźć w bibliotece SciJava Common :

ctrueden
źródło
5
+1; najlepsza jak dotąd odpowiedź: zwróci ścieżkę, używając poprawnej notacji dla systemu operacyjnego. (np. \ dla systemu Windows).
Batszeba
Jeśli chodzi o bezpieczeństwo, wydaje mi się, że Java WebStart na to nie zezwala.
Thorbjørn Ravn Andersen
55

Możesz także użyć:

CodeSource codeSource = YourMainClass.class.getProtectionDomain().getCodeSource();
File jarFile = new File(codeSource.getLocation().toURI().getPath());
String jarDir = jarFile.getParentFile().getPath();
Benny Neugebauer
źródło
3
To działa dla mnie lepiej, ponieważ daje ścieżkę do słoika, a nie do klasy!
T30
Pracowałem też dla mnie. Połącz z odpowiedzią Fab i będzie lepiej!
Danielson Alves Júnior
25

Użyj ClassLoader.getResource (), aby znaleźć adres URL bieżącej klasy.

Na przykład:

package foo;

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

(Ten przykład pochodzi z podobnego pytania ).

Aby znaleźć katalog, musisz ręcznie rozdzielić adres URL. Zobacz samouczek JarClassLoader dotyczący formatu adresu URL jar.

Jon Skeet
źródło
Mój plik JAR jest zaciemniony, więc ta odpowiedź nie rozwiązuje mojego problemu. Ale nie podałem tego w pytaniu, więc nadal jest to prawidłowa odpowiedź.
Thiago Chaves
12
Jeśli jest zaciemniony, użyj Test.class.getName () i wykonaj odpowiednie munging.
Jon Skeet
1
@JonSkeet jest tak wiele problemów z odpowiedzią: 1. Nie będzie, NPEponieważ nie odpowiedziałeś na zadane pytanie (zapytano o ścieżkę do katalogu JAR i odpowiedziałeś na zupełnie inne pytanie: ścieżka do klasy). 2. Jak zauważyli inni i mam ten sam problem, nie działa dla apletów. 3. Zwrot ścieżka nie jest kanoniczną reprezentacją ścieżki w ogóle: jar:file:/listener/build/libs/listener-1.0.0-all.jar!/shared/Test.class.
WhiteAngel
1
@WhiteAngel: 1) Ostatni wiersz mojego postu wskazuje, że musisz spojrzeć na adres URL i wybrać go osobno, aby uzyskać plik jar. Zgadzam się, że nie jest to najbardziej kompletna odpowiedź, ale nie sądzę, aby naprawdę tak źle było kłócić się (szczególnie 10 lat później ...) 2) Apletów nie wymieniono w żadnych komentarzach tutaj - o dziwo, nie nie mam czasu na przeglądanie wszystkich komentarzy do wszystkich odpowiedzi na pytania, na które zdarzyło mi się pisać odpowiedź. 3) Ponownie odsyłam do formatu adresu URL jar.
Jon Skeet,
2
@WhiteAngel: Czy to najlepsza odpowiedź, jaką kiedykolwiek napisałem? Nie. Czy to tak źle, jak sobie tego życzysz? Nie, nie sądzę. (Szczególnie jeśli chodzi o twierdzenia, które wyrzuciłeś, rzucając NPE, a tak nie jest.) Sugerowałbym, abyś dodał własną odpowiedź zamiast robić zamieszanie na ten temat. To byłoby bardziej pozytywne podejście.
Jon Skeet
19

Jestem zaskoczony, widząc, że żaden ostatnio nie zaproponował użycia Path. Tu następuje cytat: „ klasy zalicza się różne metody, które mogą być wykorzystane do uzyskania informacji o ścieżce dostępu do elementów toru, konwersja ścieżkę do innych form, albo porcji ekstraktu ścieżkiPath

Zatem dobrą alternatywą jest uzyskanie Pathobjestów jako:

Path path = Paths.get(Test.class.getProtectionDomain().getCodeSource().getLocation().toURI());
mat_boy
źródło
3
Uwaga: ścieżka jest dostępna od wersji Java 7.
Chris Forrence
15

Jedyne rozwiązanie, które działa dla mnie w systemach Linux, Mac i 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();
}
Dmitrij Trofimow
źródło
To nie zadziała. Jeśli w systemie Linux metoda toUri () wyrzuci wyjątek, a dla linuxa nie dojdziesz do części else.
Wilhelm Sorban
9

Miałem ten sam problem i rozwiązałem go w ten sposób:

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

Mam nadzieję, że ci pomogłem.

Charlie
źródło
Nie rób tego URL.getPath () nie zwraca nazwy pliku i zawiedzie w wielu okolicznościach, takich jak ścieżki plików ze spacjami.
VGR
9

Oto uaktualnienie do innych komentarzy, które wydają mi się niekompletne ze względu na specyfikę

używając względnego „folderu” poza plikiem .jar (w tej samej lokalizacji słoika):

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"));
Strefa
źródło
4
Uwaga: nie zaleca się używania URLDecoderdo dekodowania znaków specjalnych. W szczególności takie znaki +zostaną błędnie zdekodowane na spacje. Zobacz moją odpowiedź, aby poznać szczegóły.
ctrueden
Używanie znaków specjalnych w nazwach plików nie jest zalecane.
Zon
URLDecoder, pomimo swojej nazwy, służy do dekodowania adresów URL oraz nazw i wartości parametrów formularzy, a nie adresów URL.
Markiz Lorne
6

Aby uzyskać ścieżkę do uruchomienia pliku jar, przestudiowałem powyższe rozwiązania i wypróbowałem wszystkie metody, które różnią się między sobą. Jeśli te kody działają w środowisku Eclipse IDE, wszyscy powinni być w stanie znaleźć ścieżkę do pliku, w tym wskazaną klasę, i otworzyć lub utworzyć wskazany plik ze znalezioną ścieżką.

Ale jest to trudne, gdy uruchomisz uruchamiany plik jar bezpośrednio lub za pomocą wiersza polecenia, nie powiedzie się, ponieważ ścieżka pliku jar uzyskana z powyższych metod da wewnętrzną ścieżkę w pliku jar, to znaczy zawsze podaje ścieżkę tak jak

rsrc: nazwa-projektu (może powinienem powiedzieć, że jest to nazwa pakietu głównego pliku klasy - wskazanej klasy)

Nie mogę przekonwertować ścieżki rsrc: ... na ścieżkę zewnętrzną, to znaczy, kiedy plik jar jest uruchamiany poza środowiskiem IDE Eclipse, nie może uzyskać ścieżki do pliku jar.

Jedynym możliwym sposobem uzyskania ścieżki uruchamiania pliku jar poza środowiskiem Eclipse IDE jest

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

ta linia kodu może zwrócić żywą ścieżkę (w tym nazwę pliku) działającego pliku jar (należy zauważyć, że ścieżka powrotu nie jest katalogiem roboczym), ponieważ dokument java i niektóre osoby powiedziały, że zwróci ścieżki wszystkich plików klas w tym samym katalogu, ale jak moje testy, jeśli w tym samym katalogu zawierają wiele plików jar, zwraca tylko ścieżkę do uruchomionego jar (o problemie z wieloma ścieżkami rzeczywiście tak się stało w Eclipse).

phchen2
źródło
java.class.pathmoże być wielowartościowy. Jedna z tych wartości z pewnością zapewni katalog lub plik JAR, w którym znajduje się bieżąca klasa, ale który?
Markiz Lorne
Potwierdzam, próbowałem innych rozwiązań, ale nigdy nie otrzymałem nazwy pliku jar. To działa bardzo prosto! dzięki - +1
guillaume girod-vitouchkina 16.04.17
5

Inne odpowiedzi wydają się wskazywać na źródło kodu, którym jest lokalizacja pliku Jar, która nie jest katalogiem.

Posługiwać się

return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile();
BLA
źródło
Może to być katalog, jeśli ładujesz swoje klasy z systemu plików zamiast z pliku JAR, np. Podczas debugowania.
Markiz Lorne
4

wybrana powyżej odpowiedź nie działa, jeśli uruchomisz słoik, klikając go ze środowiska graficznego Gnome (nie z żadnego skryptu ani terminala).

Zamiast tego polubiłem, że następujące rozwiązanie działa wszędzie:

    try {
        return URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(), "UTF-8");
    } catch (UnsupportedEncodingException e) {
        return "";
    }
lviggiani
źródło
2
Czy próbowałeś tego w aplecie lub aplikacji. uruchomiony przy użyciu Java Web Start? Rozumiem, że w obu sytuacjach zawiedzie (nawet jeśli aplikacja jest zaufana).
Andrew Thompson,
To rozwiązanie może zwrócić tylko lokalizację „.” wewnątrz pliku JAR, a nie lokalizacji z pliku JAR.
Markiz Lorne
Uwaga: nie zaleca się używania URLDecoderdo dekodowania znaków specjalnych. W szczególności takie znaki +zostaną błędnie zdekodowane na spacje. Zobacz moją odpowiedź, aby poznać szczegóły.
ctrueden
W wiosennym bagażniku rzuciNullPointerException
Ravi Parekh
Będziesz miał, NPEjeśli w JAR nie będzie żadnych zasobów.
WhiteAngel
3

Tak naprawdę jest lepsza wersja - stara zawiodła, jeśli nazwa folderu zawierała spację.

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

Jeśli chodzi o awarie apletów, to i tak zwykle nie masz dostępu do plików lokalnych. Nie wiem dużo o JWS, ale aby obsłużyć lokalne pliki, może nie być możliwe pobranie aplikacji.?

bacup lad
źródło
Istnieje kilka wbudowanych sposobów dekodowania ścieżki. Nie musisz pisać własnego kodu.
Markiz Lorne
3

Próbowałem uzyskać ścieżkę do uruchamiania słoika

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

c: \ app> java -jar application.jar

Po uruchomieniu aplikacji jar o nazwie „application.jar” w systemie Windows w folderze „ c: \ app ” wartość zmiennej String „folder” wynosiła „ \ c: \ app \ application.jar ” i miałem problemy z testowaniem poprawność ścieżki

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

Próbowałem więc zdefiniować „test” jako:

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

aby uzyskać ścieżkę w odpowiednim formacie, takim jak „ c: \ app ” zamiast „ \ c: \ app \ application.jar ” i zauważyłem, że to działa.

TheGreatPsychoticBunny
źródło
3

Najprostszym rozwiązaniem jest podanie ścieżki jako argumentu podczas uruchamiania słoika.

Możesz to zautomatyzować za pomocą skryptu powłoki (.bat w Windows, .sh gdziekolwiek indziej):

java -jar my-jar.jar .

Kiedyś .zdać aktualny katalog roboczy.

AKTUALIZACJA

Możesz umieścić plik jar w podkatalogu, aby użytkownicy nie kliknęli go przypadkowo. Twój kod powinien również sprawdzić, aby upewnić się, że argumenty wiersza poleceń zostały dostarczone, i dostarczyć dobry komunikat o błędzie, jeśli argumentów brakuje.

Max Heiber
źródło
3

Musiałem dużo zepsuć, zanim w końcu znalazłem działające (i krótkie) rozwiązanie.
Możliwe, że jarLocationpochodzi z przedrostkiem typu file:\lub jar:file\, który można usunąć za pomocą String#substring().

URL jarLocationUrl = MyClass.class.getProtectionDomain().getCodeSource().getLocation();
String jarLocation = new File(jarLocationUrl.toString()).getParent();
Jelle
źródło
2
String path = getClass().getResource("").getPath();

Ścieżka zawsze odnosi się do zasobu w pliku jar.

ZZZ
źródło
1
Ciąg ścieżek wciąż wymaga uproszczenia. String path = new File(getClass().getResource("").getPath()).getParentFile().getParent(); File jarDir = new File(path.substring(5));
ZZZ
4
Zarówno getResource("")i getResource(".")nie powiodło się w moich testach, gdy klasa rezydowała w pliku JAR; oba wywołania zwróciły wartość null.
ctrueden
2
To rzuca NullPointerException.
Markiz 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;}

Działa dobrze w systemie Windows

Denton
źródło
1

Frustrujące jest to, że gdy programujesz w Eclipse, MyClass.class.getProtectionDomain().getCodeSource().getLocation()zwraca /binświetny katalog, ale kiedy kompilujesz go do jar, ścieżka zawiera /myjarname.jarczęść, która daje ci nielegalne nazwy plików.

Aby kod działał zarówno w ide, jak i po skompilowaniu go do jar, używam następującego fragmentu kodu:

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");
}
Alexander
źródło
1

Nie jestem do końca pewien co do innych, ale w moim przypadku nie działał z „jarem Runnable” i udało mi się to, naprawiając kody razem z odpowiedzi phchen2 i kolejnej z tego linku: Jak uzyskać ścieżkę do działającego pliku JAR? Kod:

               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");
Fahad Alkamli
źródło
1

Wypróbowałem kilka rozwiązań tam, ale żadne nie przyniosło poprawnych wyników dla (prawdopodobnie specjalnego) przypadku, w którym słój działający został wyeksportowany z „Pakowaniem bibliotek zewnętrznych” w Eclipse. Z jakiegoś powodu wszystkie rozwiązania oparte na ProtectionDomain dają w tym przypadku wartość null.

Po połączeniu powyższych rozwiązań udało mi się uzyskać następujący działający kod:

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);
DragonGamer
źródło
1

Spróbuj tego:

String path = new File("").getAbsolutePath();
Blarzek
źródło
0

Ta metoda wywołana z kodu w archiwum zwraca folder, w którym znajduje się plik .jar. Powinien działać w systemie Windows lub 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);
  } 

Pochodzi z kodu na: Określ, czy działa z JAR

Bacup Lad
źródło
3
„Powinien działać w systemie Windows lub Unix.” ale zawiedzie w każdym aplecie i każdej aplikacji. uruchomione przy użyciu JWS.
Andrew Thompson,
0

Wspomnij, że jest sprawdzany tylko w, Windowsale myślę, że działa idealnie na innych systemach operacyjnych [ Linux,MacOs,Solaris] :).


Miałem 2 .jar pliki w tym samym katalogu. Chciałem, aby z jednego .jarpliku uruchomić drugi .jarplik, który znajduje się w tym samym katalogu.

Problem polega na tym, że po uruchomieniu z cmdbieżącego katalogu jest system32.


Ostrzeżenia!

  • Poniższe wydaje się działać całkiem dobrze we wszystkich testach, które zrobiłem, nawet z nazwą folderu ;][[;'57f2g34g87-8+9-09!2#@!$%^^&()lub()%&$%^@# działa dobrze.
  • Korzystam ProcessBuilderz poniższych poniżej:

🍂 ..

//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;
    }
GOXR3PLUS
źródło
0

Ten kod działał dla mnie:

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;
  }
John Lockwood
źródło
0

Ten kod działał dla mnie w celu identyfikacji, czy program jest wykonywany w pliku JAR lub IDE:

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

Jeśli muszę uzyskać pełną ścieżkę do pliku JAR systemu Windows, używam tej metody:

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

Mój pełny kod współpracujący z aplikacją Spring Boot przy użyciu CommandLineRunnerimplementacji, aby aplikacja zawsze była uruchamiana w widoku konsoli (przez pomyłkę dwukrotnie w nazwie pliku JAR), używam następnego kodu:

@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;
        }
    }
}
Jairo Martínez
źródło
-1

Piszę w Javie 7 i testuję w Windows 7 w środowisku wykonawczym Oracle, a Ubuntu w środowisku uruchomieniowym open source. Działa to idealnie dla tych systemów:

Ścieżka do katalogu nadrzędnego dowolnego działającego pliku jar (zakładając, że klasa wywołująca ten kod jest bezpośrednim dzieckiem samego archiwum jar):

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

Zatem ścieżka do pliku foo.jar wyglądałaby następująco:

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

Ponownie nie zostało to przetestowane na żadnym komputerze Mac lub starszym systemie Windows

sudoBen
źródło
-1

getProtectionDomainPodejście może nie działać czasami przykład gdy trzeba znaleźć słoik dla niektórych klas java bazowych (np w moim przypadku StringBuilderklasy w IBM JDK), jednak płynnie następujące prace:

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, "");
}
Vasu
źródło
URL.getPath () nie robi tego, co myślisz. Wszelkie znaki specjalne będą kodowane procentowo.
VGR
-1

Mam inny sposób, aby uzyskać lokalizację ciągu klasy.

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

Łańcuch wyjściowy będzie miał postać

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

Spacje i inne znaki są obsługiwane w formie bez file:/. Więc będzie łatwiejszy w użyciu.

NoSegfault
źródło