如何获取正在运行的JAR文件的路径?

Thi*_*ves 557 java jar path executable-jar

我的代码在一个JAR文件中运行,比如说foo.jar,我需要在代码中知道运行foo.jar的文件夹.

所以,如果foo.jar在C:\FOO\,我想要获得该路径,无论我当前的工作目录是什么.

Zar*_*nen 517

return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation()
    .toURI()).getPath();
Run Code Online (Sandbox Code Playgroud)

将"MyClass"替换为您班级的名称

显然,如果您的类是从非文件位置加载的,那么这会很奇怪.

  • `toURI()`步骤对于避免特殊字符(包括空格和加号)的问题至关重要.正确的一行是:`return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI());`使用`URLDecoder`不适用于许多特殊字符.有关详细信息,请参阅下面的答案. (41认同)
  • 这不是指向jar文件而不是运行目录吗?您将不得不对结果getParentFile()执行此工作. (6认同)
  • 注意:这将返回包含jar文件名称的路径 (3认同)
  • 将此方法用于 Java 8;将此方法放在外部 Jar 中的类中,通过类路径加载,则将给出外部 jar 的路径,而不是实际运行的 Jar。 (2认同)
  • 这个答案有很多赞成票,我认为是因为日期,但现在这不起作用了,在调用新文件(URI)时你会得到一个直接的异常,.toURI也可以为空 (2认同)

小智 186

最适合我的解决方案:

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

这应该解决空格和特殊字符的问题.

  • /不一定是路径分隔符.你应该做(新文件(路径)).getParentFile().getPath(). (11认同)
  • 注意:不建议使用`URLDecoder`来解码特殊字符.特别是像`+`这样的字符会被错误地解码为空格.详情请见我的回答. (11认同)
  • 这里没有附加JAR文件名的问题.在Linux上,UTF转换似乎是与@Iviggiani one(`URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(),"UTF-8");`组合的完美解决方案.但是,我没有试过Windows. (10认同)
  • 还有一点需要注意:从Jar调用此函数时,jar的名称会附加在我的末尾,因此必须执行:path.substring(0,path.lastIndexOf("/")+ 1); (8认同)
  • 谢谢,这允许我在Linux和Windows中使用FileInputStream加载我的JAR外部文件.只需在文件名前添加解码路径...... (2认同)

ctr*_*den 148

要获得File给定的Class,有两个步骤:

  1. 转换Class为aURL
  2. 转换URL为aFile

理解这两个步骤很重要,而不是将它们混为一谈.

一旦你有了File,你可以打电话getParentFile来获取包含文件夹,如果这是你需要的.

第1步:ClassURL

正如其他答案中所讨论的,有两种主要方法可以找到与a 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框架内执行一样.相反,即使在OSGi中,该getProtectionDomain方法也能正确生成file:URL.

请注意这两个getResource("")getResource(".")在我的测试中失败了,上课的时候一个JAR文件中居住; 两个调用都返回null.所以我推荐上面显示的#2调用,因为它似乎更安全.

第2步:URLFile

无论哪种方式,一旦你有了URL,下一步就是转换为File.这是它自己的挑战; 有关详细信息,请参阅Kohsuke Kawaguchi的博客文章,但简而言之,new File(url.toURI())只要URL完全格式化,您就可以使用它.

最后,我强烈反对使用URLDecoder.URL的某些字符,:并且/特别是无效的URL编码字符.从URLDecoder Javadoc:

假设编码的字符串中的所有字符是下列之一:"a"至"Z","A"到"Z","0"至"9",和" - ","_"," ."和"*".允许使用字符"%",但将其解释为特殊转义序列的开头.

...

这种解码器有两种可能的方式来处理非法字符串.它可能会单独留下非法字符,也可能会抛出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);
}
Run Code Online (Sandbox Code Playgroud)

您可以在SciJava Common库中找到这些方法:

  • +1; 日期的最佳答案:它将使用操作系统的正确表示法返回路径.(例如\用于Windows). (5认同)

Ben*_*uer 53

您还可以使用:

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

  • 这对我来说效果更好,因为它给出了Jar的路径,而不是类的路径! (3认同)

Jon*_*eet 25

使用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"));
    }
}
Run Code Online (Sandbox Code Playgroud)

(这个例子来自一个类似的问题.)

要查找目录,您需要手动拆分URL.有关jar URL的格式,请参阅JarClassLoader教程.

  • 如果它被混淆了,请使用Test.class.getName()并进行适当的修改. (12认同)
  • @JonSkeet,你的答案有很多问题:1.不会有“NPE”,因为你没有回答所提出的问题(询问了 JAR 目录的路径,并且你回答了完全不同的问题:类的路径)。2.正如其他人所指出的,我也遇到了同样的问题,它不适用于小程序。3.返回的路径根本不是规范的路径表示:`jar:file:/listener/build/libs/listener-1.0.0-all.jar!/shared/Test.class`。 (2认同)
  • @WhiteAngel:这是我写过的最好的答案吗?不.它是否像你要成功一样糟糕?不,我不这么认为.(特别是关于你在它周围抛出NPE的声明而言,它没有.)我建议你添加自己的答案,而不是对这个问题大惊小怪.这将是一个更积极的方法. (2认同)

mat*_*boy 19

我很惊讶地看到最近没有提议使用Path.这里如下引文:" Path类包括可以用来获得关于所述路径信息的各种方法,该路径的接入元件,所述路径转换为其它形式的,或提取的路径的部分 "

因此,一个好的选择是将Path目标视为:

Path path = Paths.get(Test.class.getProtectionDomain().getCodeSource().getLocation().toURI());
Run Code Online (Sandbox Code Playgroud)

  • 注意,Path从Java 7开始可用. (3认同)

Dmi*_*mov 15

在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();
}
Run Code Online (Sandbox Code Playgroud)


Zon*_*Zon 9

这是对其他评论的升级,对于我来说,这似乎是不完整的

使用.jar文件外的相对"文件夹"(在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"));
Run Code Online (Sandbox Code Playgroud)

  • 注意:不建议使用`URLDecoder`来解码特殊字符.特别是像`+`这样的字符会被错误地解码为空格.详情请见我的回答. (4认同)

Cha*_*lie 9

我有同样的问题,我解决了这个问题:

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

我希望我对你有所帮助.


phc*_*en2 6

为了获得运行jar文件的路径,我研究了上述解决方案并尝试了所有存在一些差异的方法.如果这些代码在Eclipse IDE中运行,那么它们都应该能够找到包含指定类的文件路径,并打开或创建带有找到路径的指示文件.

但是很棘手,当直接或通过命令行运行runnable jar文件时,它会失败,因为从上面的方法获取的jar文件的路径将在jar文件中给出一个内部路径,也就是它总是给出一个路径如

rsrc:project-name(也许我应该说它是主类文件的包名 - 指示的类)

我无法将rsrc:...路径转换为外部路径,即在Eclipse IDE外部运行jar文件时无法获取jar文件的路径.

获取在Eclipse IDE外部运行jar文件的路径的唯一可能方法是

System.getProperty("java.class.path")
Run Code Online (Sandbox Code Playgroud)

这段代码行可能会返回正在运行的jar文件的生存路径(包括文件名)(注意返回路径不是工作目录),因为java文件和一些人说它会返回所有类文件的路径在同一目录中,但作为我的测试,如果在同一目录中包含许多jar文件,它只返回运行jar的路径(关于多路径问题确实发生在Eclipse中).

  • `java.class.path` 可以是多值的。*其中一个*这些值肯定会提供当前类所在的目录或 JAR 文件,但是哪一个呢? (2认同)

F.O*_*O.O 5

其他答案似乎指向代码源,它是Jar文件位置,而不是目录。

使用

return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile();
Run Code Online (Sandbox Code Playgroud)


Whi*_*ing 5

如果您真的正在寻找一种简单的方法来获取 JAR 所在的文件夹,则应该使用此实现。像这样的解决方案很难找到,许多解决方案不再受支持,许多其他解决方案提供文件的路径而不是实际目录。这比您将要找到的其他解决方案更容易,并且适用于 Java 1.12 版。

new File(".").getCanonicalPath()
Run Code Online (Sandbox Code Playgroud)

从其他答案中收集输入这也是一个简单的答案:

String localPath=new File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile().getPath()+"\\"; 
Run Code Online (Sandbox Code Playgroud)

两者都将返回具有以下格式的字符串:

"C:\Users\User\Desktop\Folder\"
Run Code Online (Sandbox Code Playgroud)

在一个简单而简洁的线条。

  • 当前工作目录并不总是与执行的jar目录相同 (2认同)