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

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

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

29 ответов


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

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

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


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

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

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


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

  1. преобразование Class до URL
  2. преобразование URL до File

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

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

Шаг 1: Class to 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 подход правильно дает file: URL даже из 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'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 библиотека:


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

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

Используется Загрузчика.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.


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


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

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


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

Вы можете автоматизировать это с помощью shell-скрипт (.летучая мышь в окнах .sh anywhere else):

java -jar my-jar.jar .

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

обновление

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


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

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

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

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

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

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


Я попытался получить путь jar, используя

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

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

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

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:\app" вместо "\c:\app\application.Джар" и я заметил, что это работает.


что-то, что расстраивает, это то, что когда вы развиваетесь в Eclipse MyClass.class.getProtectionDomain().getCodeSource().getLocation() возвращает /bin каталог, который отлично, но когда вы компилируете его в банку, путь включает /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");
}

не совсем уверен в других, но в моем случае он не работал с "Runnable jar" , и я получил его, исправляя коды вместе из phchen2 answer и другой из этой ссылки:Как получить путь к запущенному файлу 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");

этот метод, вызываемый из кода в архиве, возвращает папку, где находится .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);
  } 

производный от кода at:определить, если работает от JAR


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


Я 2 .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'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;
  }

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

вот как должно быть +1 а не -1:

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

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

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

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

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


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

Итак, путь фу.Джар будет:

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

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


на getProtectionDomain подход может не работать иногда, например, когда вам нужно найти jar для некоторых основных классов java (e.g в моем случае 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 path = Thread.currentThread().getContextClassLoader().getResource("");
Path p = Paths.get(path.toURI());
String location = p.toString();

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

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

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


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

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

эта строка работает для папок, содержащих пробелы или специальные символы (например, ç или õ). Исходный вопрос задает абсолютный путь (рабочий каталог) без самого файла JAR. Протестировано здесь с Java7 на Windows7:

String workingDir = System.getProperty("user.dir");

ссылка:http://www.mkyong.com/java/how-to-get-the-current-working-directory-in-java/