java—如何获取正在运行的jar文件的路径?

yfjy0ee7  于 2021-06-29  发布在  Java
关注(0)|答案(25)|浏览(777)

**想改进这篇文章吗?**提供这个问题的详细答案,包括引文和解释为什么你的答案是正确的。没有足够细节的答案可能会被编辑或删除。

我的代码运行在一个jar文件中,比如foo.jar,我需要知道,在代码中,运行foo.jar的文件夹在哪个文件夹中。
所以,如果foo.jar在 C:\FOO\ ,无论当前工作目录是什么,我都希望获得该路径。

iyzzxitl

iyzzxitl16#

不太清楚其他的,但在我的例子中,它不适用于“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");
1cklez4t

1cklez4t17#

获得 File 对于给定的 Class ,有两个步骤:
转换 ClassURL 转换 URLFile 重要的是要理解这两个步骤,而不是混为一谈。
一旦你有了 File ,您可以拨打 getParentFile 获取包含文件夹,如果您需要的话。

步骤1:类到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 正确的方法会产生 file: 甚至可以从osgi内部访问url。
请注意,两者 getResource("") 以及 getResource(".") 当类驻留在jar文件中时,在我的测试中失败;两个调用都返回null。因此,我建议使用上面显示的#2调用,因为它看起来更安全。

步骤2:文件的url

不管怎样,一旦你有了 URL ,下一步是转换为 File . 这是它自己的挑战;请参阅kohsuke kawaguchi的博客文章以了解详细信息,但简而言之,您可以使用 new File(url.toURI()) 只要url的格式完全正确。
最后,我强烈反对使用 URLDecoder . url的某些字符, : 以及 / 特别是,不是有效的url编码字符。从URLDock:
假设编码字符串中的所有字符都是以下字符之一:“a”到“z”、“a”到“z”、“0”到“9”以及“-”、“_u3;”、“和”*”。字符“%”是允许的,但被解释为特殊转义序列的开始。
...
这个解码器有两种可能的方法来处理非法字符串。它可以不使用非法字符,也可以抛出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公共库中找到这些方法:
org.scijava.util.classutils文件
org.scijava.util.fileutils。

utugiqy6

utugiqy618#

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

将“myclass”替换为类的名称。
显然,如果您的类是从非文件位置加载的,那么这样做会很奇怪。

mzsu5hc0

mzsu5hc019#

我们已经尝试了几种解决方案,但是没有一种能够产生正确的结果(可能是特殊的),即在eclipse中,runnable jar是通过“打包外部库”导出的。出于某种原因,在这种情况下,所有基于protectiondomain的解决方案都会导致null。
通过结合上述一些解决方案,我成功地实现了以下工作代码:

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

zbdgwd5y20#

如果你真的在寻找一种简单的方法来获取你的jar所在的文件夹,你应该实现这种格式的解决方案,它比你要找到的要容易得多(很难找到,很多解决方案已经不受支持了,许多其他人提供文件的路径而不是实际的目录),这在版本1.12上已经被证明是有效的。

new File(".").getCanonicalPath()

从其他答案中收集信息这也是一个简单的问题:

String localPath=new File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile().getPath()+"\\";

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

"C:\Users\User\Desktop\Folder\"

简洁明了。

dluptydi

dluptydi21#

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

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了解不多,但要处理本地文件,可能无法下载应用程序。?

6bc51xsx

6bc51xsx22#

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

ohtdti5x

ohtdti5x23#

我的最佳解决方案:

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

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

vtwuwzda

vtwuwzda24#

在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();
}
yquaqz18

yquaqz1825#

最简单的解决方案是在运行jar时将路径作为参数传递。
可以使用shell脚本(在windows中为.bat,在其他任何地方为.sh)自动执行此操作:

java -jar my-jar.jar .

我曾经 . 传递当前工作目录。
更新
您可能希望将jar文件粘贴到子目录中,这样用户就不会意外地单击它。您的代码还应该检查以确保提供了命令行参数,如果缺少参数,则提供良好的错误消息。

相关问题