如何获取正在运行的JAR文件的路径?
我的代码在一个JAR文件中运行,比如foo.jar,我需要知道在代码中运行foo.jar的文件夹。
所以,如果foo.jar在C:FOO
,我想要得到那个路径,不管我当前的工作目录是什么。
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");
这应该解决空间和特殊字符的问题。
要获得给定Class
的File
,有两个步骤:
Class
转换为URL
URL
转换为File
理解这两个步骤非常重要,而不是混淆它们。
一旦你有了File
,你可以调用getParentFile
来获取包含的文件夹,如果这是你需要的。
步骤1: Class
到URL
正如在其他答案中所讨论的,有两种主要方法可以找到与Class
相关的URL
。
URL url = Bar.class.getProtectionDomain().getCodeSource().getLocation();
URL url = Bar.class.getResource(Bar.class.getSimpleName() + ".class");
两者都有优点和缺点。
getProtectionDomain
方法产生类的基本位置(例如,包含的JAR文件)。 但是,在调用getProtectionDomain()
,Java运行时的安全策略可能会引发SecurityException
,因此如果您的应用程序需要在各种环境中运行,最好对它们进行测试。
getResource
方法产生类的完整URL资源路径,从中你将需要执行额外的字符串操作。 它可能是一个file:
path,但它也可能是jar:file:
或者甚至在OSGi框架内执行时,比如bundleresource://346.fwk2106232034:4/foo/Bar.class
。 相反, getProtectionDomain
方法甚至可以在OSGi内生成一个file:
URL。
请注意,在我的测试中,当类位于JAR文件中时, getResource("")
和getResource(".")
失败; 两个调用都返回null。 所以我推荐上面显示的#2调用,因为它看起来比较安全。
第2步:到File
URL
无论哪种方式,一旦你有一个URL
,下一步就是转换为一个File
。 这是它自己的挑战; 请参阅Kohsuke Kawaguchi的博客文章了解详细信息,但总之,只要URL完全格式良好,您就可以使用new File(url.toURI())
。
最后,我会极力劝阻使用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);
}
您可以在SciJava Common库中找到这些方法: