要获取给定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 文件)。但是,有可能Java运行时的安全策略在调用getProtectionDomain()时会抛出SecurityException,所以如果你的应用需要运行在各种环境中,最好在所有环境中进行测试。
getResource 方法会生成类的完整 URL 资源路径,您需要从中执行额外的字符串操作。它可能是file: 路径,但也可能是jar:file:,甚至在OSGi 框架中执行时也可能是bundleresource://346.fwk2106232034:4/foo/Bar.class 之类的更糟糕的东西。相反,getProtectionDomain 方法即使在 OSGi 中也能正确生成 file: URL。
请注意,当类驻留在 JAR 文件中时,getResource("") 和 getResource(".") 在我的测试中都失败了;两个调用都返回 null。所以我推荐上面显示的 #2 调用,因为它看起来更安全。
第 2 步:URL 到 File
无论哪种方式,一旦您拥有URL,下一步就是转换为File。这是它自己的挑战;详情请参阅Kohsuke Kawaguchi's blog post about it,但简而言之,只要网址格式完全正确,您就可以使用new File(url.toURI())。
最后,我强烈反对使用URLDecoder。 URL 的某些字符,尤其是: 和/,不是有效的 URL 编码字符。来自URLDecoderJavadoc:
假设编码字符串中的所有字符都是以下之一:“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 库中找到这些方法: