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


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

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

29 495

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 до URL

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

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

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

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

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

The 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 Кавагути пост об этом для полной информации, но вкратце, вы можете использовать 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. Далее следует цитата:"The 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 напрямую или через командную строку, он будет неудачным, так как путь к файлу 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 где-нибудь еще):

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 с именем " application.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);
  } 

производный от кода по адресу: определить, если работает из 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;
  }

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

здесь оба должны быть +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

Итак, путь фу.jar будет:

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

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

The getProtectionDomain подход может не работать иногда, например, когда вам нужно найти jar для некоторых основных классов 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 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/