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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

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

  • Преобразуйте Class в URL
  • Преобразуйте URL в File

Важно понимать оба шага и не связывать их.

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

Шаг 1: Class до URL

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

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

  • 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. Это его собственная задача; см. сообщение в блоге Kohsuke Kawaguchi об этом, но, в общем, вы можете использовать new File(url.toURI()), пока URL-адрес полностью сформирован.

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

Предполагается, что все символы в кодированной строке являются одним из следующих: "a" через "z", "A" через "Z", "0" - "9" и "-", "_", ".", а также "*". Символ "%" разрешен, но интерпретируется как начало специальной escape-последовательности.

...

Есть два возможных способа, которыми этот декодер может иметь дело с незаконными строками. Он может либо оставить незаконные символы в одиночку, либо он может вызвать исключение IllegalArgumentException. Какой подход принимает декодер, остается реализовать.

На практике 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 path from the URL string,
    // leaving the base path.

    // get the class 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 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:

Ответ 4

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

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

Ответ 5

Используйте 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.

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

используя относительную "папку" вне файла .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"));

Ответ 9

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

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

Надеюсь, я помог вам.

Ответ 10

Для получения пути запуска 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).

Ответ 11

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

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

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

Ответ 12

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

  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, но для обработки локальных файлов может не получиться скачать приложение.?

Ответ 13

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

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

java -jar my-jar.jar .

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

UPDATE

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

Ответ 14

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

Использование

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

Ответ 15

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

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

Ответ 16

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

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

Ответ 17

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

Ответ 18

Я попытался получить пробег в jar, используя

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

c:\app > java -jar application.jar

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

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

Итак, я попытался определить "test" как:

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

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

Ответ 19

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

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

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

Ответ 20

Не совсем уверен в других, но в моем случае он не работал с "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");

Ответ 21

Опробовали несколько решений, но ни одно из них не дало правильных результатов для (вероятно, особого) случая, когда исполняемый файл 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);

Ответ 22

Этот метод, вызванный из кода в архиве, возвращает папку, в которой находится файл .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

Ответ 23

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


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

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


предупреждения!

  • Ниже показано, что все работает хорошо во всех тестах, которые я сделал даже с именем папки ;][[;'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 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;
    }

Ответ 24

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

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

Ответ 25

Если баночка работает, то путь к банке можно получить с помощью следующего кода:

public class JarpathTest
{
    public static void main(String[] args)
    {
        File jarDir = new File(ClassLoader.getSystemClassLoader().getResource(".").getPath());
        System.out.println(jarDir.getAbsolutePath());
    }
}

Ответ 26

Игнорировать ответ резервного помощника, он может выглядеть нормально, но имеет несколько проблем:

здесь оба должны быть +1 не -1:

name = name.substring(name.lastIndexOf(':')-1, name.lastIndexOf('/')+1).replace('%', ' ');

Очень опасно, потому что не сразу видно, если путь не имеет пробелов, но замена только "%" оставит вас с пучком по 20 в каждом пробеле:

name = name.substring(name.lastIndexOf(':')-1, name.lastIndexOf('/')+1).replace('%', ' ');

Есть более эффективные пути, чем этот цикл для пробелов.

Также это вызовет проблемы во время отладки.

Ответ 27

Я пишу в 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

Ответ 28

Подход 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, "");
}

Ответ 29

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

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

Строка вывода будет иметь вид

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

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

Ответ 30

Или вы можете передать текущий поток таким образом:

String myPath = Thread.currentThread().getContextClassLoader().getResource("filename").getPath();