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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/320542/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 13:07:54  来源:igfitidea点击:

How to get the path of a running JAR file?

javapathjarexecutable-jar

提问by Thiago Chaves

My code runs inside a JAR file, say foo.jar, and I need to know, in the code, in which folder the running foo.jaris.

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

So, if foo.jaris in C:\FOO\, I want to get that path no matter what my current working directory is.

因此,如果foo.jar在 中C:\FOO\,无论我当前的工作目录是什么,我都想获得该路径。

采纳答案by Zarkonnen

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

Replace "MyClass" with the name of your class.

用您的班级名称替换“MyClass”。

Obviously, this will do odd things if your class was loaded from a non-file location.

显然,如果您的类是从非文件位置加载的,这会做一些奇怪的事情。

回答by Jon Skeet

Use ClassLoader.getResource() to find the URL for your current class.

使用 ClassLoader.getResource() 查找当前类的 URL。

For example:

例如:

package foo;

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

(This example taken from a similar question.)

(这个例子取自一个类似的问题。)

To find the directory, you'd then need to take apart the URL manually. See the JarClassLoader tutorialfor the format of a jar URL.

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

回答by ZZZ

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

The path always refers to the resource within the jar file.

路径总是指 jar 文件中的资源。

回答by Bacup Lad

This method, called from code in the archive, returns the folder where the .jar file is. It should work in either Windows or Unix.

此方法从存档中的代码调用,返回 .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);
  } 

Derived from code at: Determine if running from JAR

从代码派生:确定是否从 JAR 运行

回答by bacup lad

Actually here is a better version - the old one failed if a folder name had a space in it.

实际上这是一个更好的版本 - 如果文件夹名称中有空格,旧版本就会失败。

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

As for failing with applets, you wouldn't usually have access to local files anyway. I don't know much about JWS but to handle local files might it not be possible to download the app.?

至于小程序失败,您通常无论如何都无法访问本地文件。我对 JWS 了解不多,但要处理本地文件,可能无法下载该应用程序。?

回答by Fab

Best solution for me:

对我来说最好的解决方案:

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

This should solve the problem with spaces and special characters.

这应该可以解决空格和特殊字符的问题。

回答by Benny Neugebauer

You can also use:

您还可以使用:

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

回答by lviggiani

the selected answer above is not working if you run your jar by click on it from Gnome desktop environment (not from any script or terminal).

如果您通过从 Gnome 桌面环境(不是从任何脚本或终端)单击来运行 jar,则上面选择的答案不起作用。

Instead, I have fond that the following solution is working everywhere:

相反,我喜欢以下解决方案在任何地方都有效:

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

回答by Zon

Here's upgrade to other comments, that seem to me incomplete for the specifics of

这是其他评论的升级,在我看来,这些评论的具体细节不完整

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

使用 .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"));

回答by ctrueden

To obtain the Filefor a given Class, there are two steps:

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

  1. Convert the Classto a URL
  2. Convert the URLto a File
  1. 转换ClassURL
  2. 转换URLFile

It is important to understand both steps, and not conflate them.

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

Once you have the File, you can call getParentFileto get the containing folder, if that is what you need.

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

Step 1: Classto URL

第 1 步:ClassURL

As discussed in other answers, there are two major ways to find a URLrelevant to a Class.

正如其他答案中所讨论的,有两种主要方法可以找到与 aURL相关的Class.

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

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

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

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

Both have pros and cons.

两者都有优点和缺点。

The getProtectionDomainapproach yields the base location of the class (e.g., the containing JAR file). However, it is possible that the Java runtime's security policy will throw SecurityExceptionwhen calling getProtectionDomain(), so if your application needs to run in a variety of environments, it is best to test in all of them.

getProtectionDomain方法产生类的基本位置(例如,包含 JAR 文件)。但是,Java 运行时的安全策略SecurityException在调用时可能会抛出getProtectionDomain()异常,因此如果您的应用程序需要在多种环境中运行,最好在所有环境中进行测试。

The getResourceapproach yields the full URL resource path of the class, from which you will need to perform additional string manipulation. It may be a file:path, but it could also be jar:file:or even something nastier like bundleresource://346.fwk2106232034:4/foo/Bar.classwhen executing within an OSGi framework. Conversely, the getProtectionDomainapproach correctly yields a file:URL even from within OSGi.

getResource方法生成类的完整 URL 资源路径,您需要从中执行额外的字符串操作。它可能是一条file:路径,但也可能是在 OSGi 框架内执行时,jar:file:甚至是更讨厌的东西bundleresource://346.fwk2106232034:4/foo/Bar.class。相反,该getProtectionDomain方法file:即使在 OSGi 中也能正确生成URL。

Note that both getResource("")and getResource(".")failed in my tests, when the class resided within a JAR file; both invocations returned null. So I recommend the #2 invocation shown above instead, as it seems safer.

请注意,当类驻留在 JAR 文件中时,我的测试中getResource("")和 都getResource(".")失败了;两次调用都返回 null。所以我推荐上面显示的 #2 调用,因为它看起来更安全。

Step 2: URLto File

第 2 步:URLFile

Either way, once you have a URL, the next step is convert to a File. This is its own challenge; see Kohsuke Kawaguchi's blog post about itfor full details, but in short, you can use new File(url.toURI())as long as the URL is completely well-formed.

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

Lastly, I would highly discourageusing URLDecoder. Some characters of the URL, :and /in particular, are not valid URL-encoded characters. From the URLDecoderJavadoc:

最后,我非常不鼓励使用URLDecoder. 该URL的一些人物:/特别,不是有效的URL编码字符。来自URLDecoderJavadoc:

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.

假设编码字符串中的所有字符都是以下字符之一:“a”到“z”、“A”到“Z”、“0”到“9”以及“-”、“_”、“ 。“, 和 ”*”。字符“%”是允许的,但被解释为特殊转义序列的开始。

...

这个解码器有两种可能的方式来处理非法字符串。它可以不理会非法字符,也可以抛出 IllegalArgumentException。解码器采用哪种方法取决于实现。

In practice, URLDecodergenerally does not throw IllegalArgumentExceptionas threatened above. And if your file path has spaces encoded as %20, this approach may appear to work. However, if your file path has other non-alphameric characters such as +you will have problems with URLDecodermangling your file path.

在实践中,URLDecoder一般不会IllegalArgumentException像上面威胁的那样扔。如果您的文件路径中的空格编码为%20,则此方法似乎有效。但是,如果您的文件路径具有其他非字母数字字符,例如+您将在URLDecoder修改文件路径时遇到问题。

Working code

工作代码

To achieve these steps, you might have methods like the following:

要实现这些步骤,您可能有如下方法:

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

You can find these methods in the SciJava Commonlibrary:

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