Как получить путь к запущенному файлу JAR?

Мой код выполняется внутри файла JAR, скажем, foo.jar, и мне нужно знать в коде, в какой папке находится запущенный foo.jar.

Итак, если foo.jar находится в C:\FOO\, я хочу получить этот путь независимо от того, какой у меня текущий рабочий каталог.

См. Ответ Fab для решения, которое работает, когда пути включают пробелы. Также обратите внимание, что некоторые ответы ниже относятся к вопросу в заголовке (путь к jar), некоторые относятся к самому вопросу (путь к папке, содержащей jar), а некоторые предоставляют пути к классам внутри файла jar.

Andy Thomas 21.12.2011 03:21

Будьте осторожны при использовании в ANT! ============== Я вызываю String path = SomeClass.class.getProtectionDomain (). GetCodeSource (). GetLoc‌ ation (). GetPath (); и получаем: /C:/apache-ant-1.7.1/lib/ant.jar Не очень полезно!

Dino Fancellu 27.01.2012 16:09

Интересно. Исходный код, в котором я это использовал, никогда не запускался в ant, поэтому для меня это не проблема.

Thiago Chaves 27.01.2012 22:01

@Dino Fancellu, я испытал именно то, что вы описали. Работает во время разработки, не работает при сборке в jar.

Buddy 02.10.2015 14:37
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
599
4
522 146
31
Перейти к ответу Данный вопрос помечен как решенный

Ответы 31

Используйте ClassLoader.getResource (), чтобы найти URL-адрес вашего текущего класса.

Например:

package foo;

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

(Этот пример взят из аналогичный вопрос.)

Чтобы найти каталог, вам нужно будет вручную разделить URL-адрес. См. Руководство по JarClassLoader для формата URL-адреса jar.

Мой файл JAR запутан, поэтому этот ответ не решает мою проблему. Но я не уточнил это в вопросе, так что это все еще действительный ответ.

Thiago Chaves 03.06.2009 18:43

Если он запутан, используйте Test.class.getName () и выполните соответствующие действия.

Jon Skeet 03.06.2009 18:53

Если ресурсов нет, выкидывается NPE

WhiteAngel 17.10.2018 09:35

@WhiteAngel: Не должно. Я ожидал, что вместо этого напечатает null, поскольку loader.getResource вернет null. Если вы попытаетесь разыменовать результат вместо передачи его в System.out.println, потом, он выдаст NPE. Однако довольно редко вы не знаете, какие ресурсы доступны - и в тех случаях, когда вы не это знаете, вы можете просто добавить нулевую проверку обычным способом.

Jon Skeet 17.10.2018 10:05

@JonSkeet Итак, вы думаете, что ваше решение, которое работает в каком-то конкретном случае, является жизнеспособным решением?

WhiteAngel 17.10.2018 10:11

@WhiteAngel: Я думаю, что решение, которое работает за вопрос, который был задан, в порядке. Можете ли вы привести пример, когда запрос ClassLoader класса для ресурса этого класса вернет null? Это вопрос, который задавали. Дело не в каком-то произвольном ресурсе, который может присутствовать или отсутствовать. (И, как я уже указывал ранее, представленный мной код не будет вызывать NPE. Если вы его видите, то это будет в каком-то другом коде, разыменовавшем результат.)

Jon Skeet 17.10.2018 10:27

@JonSkeet, так много проблем с вашим ответом: 1. NPE не будет, потому что вы не ответили на заданный вопрос (был задан путь к каталогу JAR, и вы ответили на совершенно другой вопрос: путь к классу). 2. Как указали другие, и у меня такая же проблема, это не работает для апплетов. 3. Возвращенный путь вообще не соответствует каноническому представлению пути: jar:file:/listener/build/libs/listener-1.0.0-all.jar!/shared‌​/Test.class.

WhiteAngel 17.10.2018 10:38

@WhiteAngel: 1) Последняя строка моего сообщения указывает, что вам нужно посмотреть URL-адрес и выделить его, чтобы получить файл jar. Я согласен, что это не самый полный ответ, но я не думаю, что это действительно так плохо, чтобы о нем стоило спорить (особенно 10 лет спустя ...) 2) Апплеты не упоминались ни в каких комментариях здесь - как ни странно, я не У меня нет времени просматривать все комментарии ко всем ответам на вопросы, на которые мне довелось публиковать ответ. 3) Опять же, я ссылаюсь на формат URL-адреса jar.

Jon Skeet 17.10.2018 10:44

@WhiteAngel: Это лучший ответ, который я когда-либо писал? Неа. Это так плохо, как вы думаете? Нет, не думаю. (В частности, с точки зрения заявлений, которые вы сделали по поводу того, что он бросил NPE, чего нет.) Я бы посоветовал вам добавить свой собственный ответ вместо того, чтобы суетиться по этому поводу. Это был бы более позитивный подход.

Jon Skeet 17.10.2018 10:46
Ответ принят как подходящий
return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation()
    .toURI()).getPath();

Замените MyClass именем вашего класса.

Очевидно, это будет делать странные вещи, если ваш класс был загружен из нефайлового местоположения.

Шаг toURI() жизненно важен, чтобы избежать проблем со специальными символами, включая пробелы и плюсы. Правильный однострочный текст: return new File(MyClass.class.getProtectionDomain().getCodeSource().get‌​Location().toURI()); Использование URLDecoder не работает для многих специальных символов. См. Мой ответ ниже для получения дополнительной информации.

ctrueden 22.11.2012 00:25

toURI (), похоже, прерывает пути, идущие от общего сетевого ресурса.

Brain2000 24.02.2015 02:15

Примечание: это возвращает путь, включая имя файла jar

Buddy 02.10.2015 14:36

Разве это не указывает на файл jar, а не на рабочий каталог? Для этой работы вам нужно будет выполнить функцию getParentFile ().

F.O.O 10.06.2016 14:53

@ F.O.O. [A] Да, это указывает на файл JAR [B] Нет, он не имеет отношения к рабочему каталогу. Докажите это, запустив java -Duser.dir=TotallyDiffDir -cp [myjarpath] [myappclasspath]: 'user.dir' устанавливает текущий рабочий каталог, и этот ответ возвращает, где код находится в пути к классам.

Raymond Naseef 11.11.2017 00:33

в чем смысл вызова getPath ()? Почему бы просто не создать файл из URI?

Xerus 02.01.2018 15:12

Кроме того, getProtectionDomain имеет значение null, если вы получаете свой класс из stracktrace: val strace = Thread.currentThread().getStackTrace; val path = strace(1).getClass.getProtectionDomain

bbarker 01.04.2018 16:40

Использование этого метода с Java 8; поместив этот метод в класс, который находится во внешнем Jar, загруженном через class-path, тогда вместо фактического запущенного Jar будет указан путь к внешнему jar.

Mr00Anderson 26.02.2019 19:06

у этого ответа много апвотов, я думаю, из-за даты, но теперь, когда это не работает, вы получите прямое исключение при вызове нового файла (URI), .toURI также может быть нулевым

P. Waksman 18.12.2020 00:06
String path = getClass().getResource("").getPath();

Путь всегда относится к ресурсу в файле jar.

Эту строку пути по-прежнему необходимо упростить. String path = new File(getClass().getResource("").getPath()).getParentFile().g‌​etParent(); File jarDir = new File(path.substring(5));

ZZZ 16.11.2010 00:59

И getResource(""), и getResource(".") не прошли мои тесты, когда класс находился в файле JAR; оба вызова вернули null.

ctrueden 04.12.2012 20:50

Это выкидывает NullPointerException.

user207421 16.12.2016 03:06

Этот метод, вызываемый из кода в архиве, возвращает папку, в которой находится файл .jar. Он должен работать как в Windows, так и в 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);
  } 

Получено из кода по адресу: Определите, работает ли он из JAR

«Он должен работать либо в Windows, либо в Unix». но потерпит неудачу в любом апплете и каждом приложении. запускается с помощью JWS.

Andrew Thompson 14.04.2011 23:36

На самом деле это лучшая версия - старая не работала, если в имени папки был пробел.

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

Что касается сбоев с апплетами, у вас в любом случае обычно не будет доступа к локальным файлам. Я мало что знаю о JWS, но для обработки локальных файлов может быть невозможно загрузить приложение.?

Есть несколько встроенных способов декодирования пути. Не нужно писать собственный код.

user207421 16.12.2016 03:04

Лучшее решение для меня:

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

Это должно решить проблему с пробелами и специальными символами.

Еще одно замечание: при вызове этой функции из Jar имя баночки добавляется в конце для меня, поэтому мне пришлось выполнить: path.substring (0, path.lastIndexOf ("/") + 1);

will824 05.10.2011 19:29

/ не обязательно является разделителем пути. Вместо этого вы должны сделать (новый файл (путь)). GetParentFile (). GetPath ().

pjz 30.03.2012 01:36

Нет проблем с добавлением сюда имени файла JAR. Преобразование UTF кажется идеальным решением в сочетании с @Iviggiani (URLDecoder.decode(ClassLoader.getSystemClassLoader().getRes‌​ource(".").getPath()‌​, "UTF-8");) в Linux. Однако в Windows я не пробовал.

ubuntudroid 03.04.2012 15:35

Спасибо, это позволило мне загружать файлы, внешние по отношению к моему JAR, с помощью FileInputStream как в Linux, так и в Windows. Просто нужно было добавить декодированный путь перед именем файла ...

giorgio79 12.05.2012 10:49

Я регулярно упаковываю файлы в свои исполняемые jar-файлы (исполняемые файлы, чаще всего с настройками по умолчанию), чтобы создать небольшие служебные инструменты. Для меня важнее всего иметь надежный инструмент, который не требует внешней информации и не полагается ни на что, кроме самого себя, так что это отличный фрагмент для меня! Спасибо

K.Barad 28.06.2012 15:59

Осторожно: не рекомендуется использовать URLDecoder для декодирования специальных символов. В частности, такие символы, как +, будут ошибочно декодированы в пробелы. Подробности смотрите в моем ответе.

ctrueden 30.10.2012 22:08

Это лучшее решение (Fab's) ... Я написал String path = URLDecoder.decode ([MyClass] .class.getProtectionDomain (). GetC‌ odeSource (). GetLocat‌ ion (). GetPath (), "UTF-8 ");

Lilian A. Moraru 14.03.2013 03:06

@ LilianA.Moraru Это эквивалентно.

nanofarad 26.07.2013 19:12

Это решение также работает с сетевым ресурсом, в другом ответе использовалась функция toURI (), которая отсекает первую часть пути к общему ресурсу.

Brain2000 24.02.2015 02:15

Это решение не работает в Windows. Однако принятый ответ работает.

krzychu 19.03.2020 20:00

Если вы используете URLDecoder.decode(path, StandardCharsets.UTF_8), представленный в Java 10, вам не придется обращаться с UnsupportedEncodingException.

Abhijit Sarkar 09.01.2021 02:20

Вы также можете использовать:

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

Это работает лучше для меня, потому что он дает путь к Jar, а не к классу!

T30 04.03.2014 17:44

У меня тоже сработало. Объедините с ответом Fab, и он станет лучше!

Danielson Alves Júnior 10.02.2020 17:47

выбранный выше ответ не работает, если вы запустите свою банку, щелкнув ее в среде рабочего стола Gnome (не из любого скрипта или терминала).

Вместо этого мне нравится, что следующее решение работает везде:

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

Вы пробовали это в апплете или приложении? запущен с использованием Java Web Start? Насколько я понимаю, он потерпит неудачу в обеих ситуациях (даже если приложению доверяют).

Andrew Thompson 28.09.2011 11:56

Это решение может возвращать только местоположение "." в пределах файла JAR, а не расположение из файла JAR.

user207421 06.03.2012 07:11

Осторожно: не рекомендуется использовать URLDecoder для декодирования специальных символов. В частности, такие символы, как +, будут ошибочно декодированы в пробелы. Подробности смотрите в моем ответе.

ctrueden 30.10.2012 22:09

При загрузке Spring выкинет NullPointerException

Ravi Parekh 20.12.2017 09:23

У вас будет NPE, если в JAR нет ресурсов.

WhiteAngel 17.10.2018 09:39

Вот обновление других комментариев, которые кажутся мне неполными из-за специфики

using a relative "folder" outside .jar file (in the jar's same location):

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

Осторожно: не рекомендуется использовать URLDecoder для декодирования специальных символов. В частности, такие символы, как +, будут ошибочно декодированы в пробелы. Подробности смотрите в моем ответе.

ctrueden 30.10.2012 22:09

Не рекомендуется использовать специальные символы в именах файлов.

Zon 15.07.2013 14:41
URLDecoder, несмотря на свое название, предназначен для декодирования имен и значений URL и параметров формы, а не URL.
user207421 16.12.2016 03:05

Чтобы получить File для данного Class, необходимо выполнить два шага:

  1. Преобразуйте Class в URL
  2. Преобразуйте URL в File

Важно понимать оба шага, а не объединять их.

Если у вас есть File, вы можете вызвать в getParentFile, чтобы получить содержащую папку, если это то, что вам нужно.

Шаг 1: Class - URL

Как обсуждалось в других ответах, есть два основных способа найти URL, имеющий отношение к Class.

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

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

У обоих есть плюсы и минусы.

Подход getProtectionDomain дает базовое местоположение класса (например, содержащий JAR-файл). Однако возможно, что политика безопасности среды выполнения Java вызовет SecurityException при вызове getProtectionDomain(), поэтому, если ваше приложение должно работать в различных средах, лучше всего протестировать во всех из них.

Подход getResource дает полный путь ресурса URL-адреса класса, из которого вам нужно будет выполнить дополнительные манипуляции со строкой. Это может быть путь file:, но также может быть jar:file: или даже что-то более неприятное, например bundleresource://346.fwk2106232034:4/foo/Bar.class, при выполнении в среде OSGi. И наоборот, подход getProtectionDomain правильно выдает URL-адрес file: даже изнутри OSGi.

Обратите внимание, что и getResource(""), и getResource(".") не прошли мои тесты, когда класс находился в файле JAR; оба вызова вернули null. Поэтому я рекомендую вместо этого вызов №2, показанный выше, поскольку он кажется более безопасным.

Шаг 2: URL - File

В любом случае, если у вас есть URL, следующим шагом будет преобразование в File. Это его собственный вызов; см. Сообщение в блоге Косуке Кавагути об этом для получения полной информации, но вкратце, вы можете использовать new File(url.toURI()), если URL-адрес полностью правильно сформирован.

Наконец, я бы использовал сильно обескураживать, используя URLDecoder. Некоторые символы URL-адреса, в частности : и /, не являются допустимыми символами в кодировке URL. Из документации URLDecoder Javadoc:

It is assumed that all characters in the encoded string are one of the following: "a" through "z", "A" through "Z", "0" through "9", and "-", "_", ".", and "*". The character "%" is allowed but is interpreted as the start of a special escaped sequence.

...

There are two possible ways in which this decoder could deal with illegal strings. It could either leave illegal characters alone or it could throw an IllegalArgumentException. Which approach the decoder takes is left to the implementation.

На практике URLDecoder обычно не бросает IllegalArgumentException, как указано выше. И если ваш путь к файлу имеет пробелы, закодированные как %20, этот подход может работать. Однако, если ваш путь к файлу содержит другие не-буквенные символы, такие как +, у вас будут проблемы с URLDecoder, искажающим ваш путь к файлу.

Рабочий код

Для выполнения этих шагов у вас могут быть следующие методы:

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

Вы можете найти эти методы в библиотеке SciJava Common:

+1; лучший ответ на сегодняшний день: он вернет путь, используя правильную нотацию для ОС. (например, \ для окон).

Bathsheba 05.09.2013 12:47

Что касается безопасности, я считаю, что обнаружил, что Java WebStart не позволяет этого.

Thorbjørn Ravn Andersen 08.01.2019 19:22
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;}

Хорошо работает в Windows

У меня была такая же проблема, и я решил ее таким образом:

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

Надеюсь, я был вам полезен.

Не делай этого. URL.getPath () не возвращает имя файла, и он не работает во многих обстоятельствах, таких как пути к файлам с пробелами в них.

VGR 13.06.2016 18:32

Единственное решение, которое работает для меня в Linux, Mac и 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();
}

Так не пойдет. Если в Linux метод toUri () вызовет исключение, и вы не дойдете до другой части для Linux.

Wilhelm Sorban 09.01.2018 20:45

сломан для анонимного класса

martian 23.07.2020 15:31

Я удивлен, увидев, что в последнее время никто не предлагал использовать Path. Здесь следует цитата: "Класс Path включает в себя различные методы, которые можно использовать для получения информации о пути, доступа к элементам пути, преобразования пути в другие формы или извлечения частей пути."

Таким образом, хорошей альтернативой является получение объекта Path как:

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

Обратите внимание, что Path доступен начиная с Java 7.

Chris Forrence 23.10.2013 21:05

Я попытался получить путь к банке, используя

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

c: \ приложение> java -jar application.jar

Запуск приложения jar с именем «application.jar» в Windows в папке «c: \ приложение», значение строковой переменной «folder» было «\ c: \ приложение \ application.jar», и у меня возникли проблемы с проверкой правильности пути

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

Поэтому я попытался определить «тест» как:

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

чтобы получить путь в правильном формате, например «c: \ приложение» вместо «\ c: \ приложение \ application.jar», и я заметил, что он работает.

Я пишу на Java 7 и тестирую в Windows 7 со средой выполнения Oracle и Ubuntu со средой выполнения с открытым исходным кодом. Это идеально подходит для этих систем:

Путь к родительскому каталогу любого запущенного файла jar (при условии, что класс, вызывающий этот код, является прямым потомком самого архива 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

Итак, путь к foo.jar будет таким:

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

Опять же, это не тестировалось ни на одном Mac или более старых версиях Windows.

Подход getProtectionDomain может иногда не работать, например. когда вам нужно найти банку для некоторых основных классов java (например, в моем случае класс StringBuilder в IBM JDK), однако следующее работает без проблем:

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 () не делает то, что вы думаете. Любые специальные символы будут закодированы в процентах.

VGR 13.06.2016 18:39

У меня есть другой способ получить расположение класса String.

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

Выходная строка будет иметь вид
.

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

Пробелы и другие символы обрабатываются, причем в форме без file:/. Так будет проще пользоваться.

Самое простое решение - передать путь в качестве аргумента при запуске jar.

Вы можете автоматизировать это с помощью сценария оболочки (.bat в Windows, .sh в любом другом месте):

java -jar my-jar.jar .

Я использовал . для передачи текущего рабочего каталога.

ОБНОВИТЬ

Вы можете поместить файл jar в подкаталог, чтобы пользователи случайно не щелкнули его. Ваш код также должен проверять, указаны ли аргументы командной строки, и выдавать хорошее сообщение об ошибке, если аргументы отсутствуют.

Чтобы получить путь к запущенному файлу jar, я изучил приведенные выше решения и попробовал все методы, которые имеют некоторые отличия друг от друга. Если этот код выполняется в Eclipse IDE, все они должны иметь возможность найти путь к файлу, включая указанный класс, и открыть или создать указанный файл с найденным путем.

Но это сложно, когда запускаемый jar-файл напрямую или через командную строку, он потерпит неудачу, поскольку путь к jar-файлу, полученному из вышеуказанных методов, даст внутренний путь в файле jar, то есть он всегда дает путь как

rsrc: имя-проекта (возможно, я должен сказать, что это имя пакета основного файла класса - указанного класса)

Я не могу преобразовать путь rsrc: ... во внешний путь, то есть при запуске файла jar вне Eclipse IDE он не может получить путь к файлу jar.

Единственный возможный способ получить путь к запускаемому jar-файлу вне Eclipse IDE - это

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

эта строка кода может возвращать живой путь (включая имя файла) запущенного файла jar (обратите внимание, что путь возврата не является рабочим каталогом), поскольку документ java и некоторые люди сказали, что он вернет пути ко всем файлам классов в том же каталоге, но, как и в моих тестах, если в одном каталоге содержится много файлов jar, он возвращает только путь к запущенному jar (что касается проблемы с несколькими путями, это действительно произошло в Eclipse).

java.class.path может быть многозначным. Один эти значения обязательно предоставят каталог или файл JAR, где находится текущий класс, но какой?
user207421 16.12.2016 03:06

Подтверждаю, я пробовал другие решения, но так и не получил имя файла jar. Работает это очень просто! спасибо - +1

guillaume girod-vitouchkina 16.04.2017 13:18

Что расстраивает, так это то, что когда вы разрабатываете в Eclipse, MyClass.class.getProtectionDomain().getCodeSource().getLocation() возвращает каталог /bin, и это здорово, но когда вы компилируете его в jar, путь включает часть /myjarname.jar, которая дает вам недопустимые имена файлов.

Чтобы код работал как в ide, так и после компиляции в jar, я использую следующий фрагмент кода:

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

Другие ответы, похоже, указывают на источник кода, который является местоположением файла Jar, который не является каталогом.

Использовать

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

Это может быть каталог, если вы загружаете свои классы из файловой системы вместо файла JAR, например. при отладке.

user207421 16.12.2016 03:07

Не совсем уверен насчет других, но в моем случае он не работал с «Runnable jar», и я получил его, исправив коды вместе из ответа phchen2 и другого из этой ссылки: Как получить путь к запущенному файлу JAR? Код:

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

Мне пришлось немало возиться, прежде чем я наконец нашел рабочее (и короткое) решение.
Возможно, что jarLocation имеет префикс типа file:\ или jar:file\, который можно удалить с помощью String#substring().

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

Обратите внимание, что это проверено только в Windows, но я думаю, что он отлично работает в других операционных системах [Linux,MacOs,Solaris] :).


У меня были файлы 2.jar в том же каталоге. Я хотел из одного файла .jar запустить другой файл .jar, который находится в том же каталоге.

Проблема в том, что при запуске с cmd текущий каталог - system32.


Warnings!

  • Приведенное ниже, кажется, работает очень хорошо во всех тестах, которые я провел, с именем папки ;][[;'57f2g34g87-8+9-09!2#@!$%^^&() или ()%&$%^@# это работает хорошо.
  • Я использую ProcessBuilder со следующим:

? ..

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

Этот код работал у меня:

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

Пробовали несколько решений наверху, но ни одно из них не дало правильных результатов для (вероятно, особого) случая, когда исполняемый jar был экспортирован с помощью «Упаковка внешних библиотек» в Eclipse. По какой-то причине все решения, основанные на ProtectionDomain, в этом случае приводят к нулю.

Комбинируя несколько вышеперечисленных решений, мне удалось получить следующий рабочий код:

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

Попробуй это:

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

Этот код помог мне определить, выполняется ли программа внутри файла JAR или 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;
    }
}

Если мне нужно получить полный путь Windows к JAR-файлу, я использую этот метод:

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

Мой полный код, работающий с приложением Spring Boot с использованием реализации CommandLineRunner, чтобы гарантировать, что приложение всегда будет выполняться в представлении консоли (двойной щелчок по ошибке в имени файла JAR), я использую следующий код:

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

Если вы действительно ищете простой способ получить папку, в которой находится ваш JAR, вам следует реализовать этот формат решения. Это проще, чем то, что вы собираетесь найти (его трудно найти, и многие решения больше не поддерживаются, многие другие предоставляют путь к файлу вместо фактического каталога), и это доказано, работая в версии 1.12.

new File(".").getCanonicalPath()

Сбор данных из других ответов тоже прост:

String localPath=new File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile().getPath()+"\\"; 

Оба вернут строку в этом формате:

"C:\Users\User\Desktop\Folder\"

В простой и лаконичной строке.

Вышеупомянутые методы не работали для меня в моей среде Spring, поскольку Spring затеняет фактические классы в пакет с именем BOOT-INF, таким образом, не фактическое расположение запущенного файла. Я нашел другой способ получить текущий файл через объект Permissions, который был предоставлен текущему файлу:


public static Path getEnclosingDirectory() {
    return Paths.get(FileUtils.class.getProtectionDomain().getPermissions()
            .elements().nextElement().getName()).getParent();
}

Другие вопросы по теме