Para obtener el File
para un dado Class
, hay dos pasos:
- Convertir el
Class
aURL
- Convierte el
URL
aFile
Es importante comprender ambos pasos y no combinarlos.
Una vez que tenga el File
, puede llamargetParentFile
para obtener la carpeta que lo contiene, si eso es lo que necesita.
Paso 1: Class
aURL
Como se discutió en otras respuestas, hay dos formas principales de encontrar una URL
relevante para a Class
.
URL url = Bar.class.getProtectionDomain().getCodeSource().getLocation();
URL url = Bar.class.getResource(Bar.class.getSimpleName() + ".class");
Ambos tienen pros y contras.
El getProtectionDomain
enfoque produce la ubicación base de la clase (por ejemplo, el archivo JAR que contiene). Sin embargo, es posible que la política de seguridad del tiempo de ejecución de Java se ejecute SecurityException
al llamar getProtectionDomain()
, por lo que si su aplicación necesita ejecutarse en una variedad de entornos, lo mejor es probar en todos ellos.
El getResource
enfoque produce la ruta de recursos de URL completa de la clase, desde la cual deberá realizar una manipulación de cadena adicional. Puede ser un file:
camino, pero también podría ser jar:file:
o incluso algo más desagradable bundleresource://346.fwk2106232034:4/foo/Bar.class
cuando se ejecuta dentro de un marco OSGi. Por el contrario, el getProtectionDomain
enfoque produce correctamente unfile:
URL incluso desde OSGi.
Tenga en cuenta que tanto getResource("")
y getResource(".")
fallaron en mis pruebas, cuando la clase residía dentro de un archivo JAR; ambas invocaciones volvieron nulas. Por lo tanto, recomiendo la invocación n. ° 2 que se muestra arriba, ya que parece más segura.
Paso 2: URL
aFile
De cualquier manera, una vez que tenga un URL
, el siguiente paso es convertirlo en a File
. Este es su propio desafío; vea la publicación del blog de Kohsuke Kawaguchi al respecto para obtener detalles completos, pero en resumen, puede usarnew File(url.toURI())
siempre que la URL esté completamente bien formada.
Por último, desalentaría mucho el uso URLDecoder
. Algunos caracteres de la URL, :
y /
en particular, no son caracteres válidos codificados con URL. Desde el Javadoc URLDecoder :
Se supone que todos los caracteres en la cadena codificada son uno de los siguientes: "a" a "z", "A" a "Z", "0" a "9" y "-", "_", " . "y" * ". El carácter "%" está permitido pero se interpreta como el inicio de una secuencia especial de escape.
...
Hay dos formas posibles en que este decodificador puede manejar cadenas ilegales. Podría dejar solo caracteres ilegales o podría generar una IllegalArgumentException. El enfoque que tome el decodificador se deja a la implementación.
En la práctica, URLDecoder
generalmente no arroja IllegalArgumentException
como se amenaza anteriormente. Y si su ruta de archivo tiene espacios codificados como %20
, este enfoque puede parecer que funciona. Sin embargo, si la ruta de su archivo tiene otros caracteres no alfabéticos, como por ejemplo +
, tendrá problemas para URLDecoder
cambiar la ruta de su archivo.
Código de trabajo
Para lograr estos pasos, puede tener métodos como los siguientes:
/**
* 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);
}
Puede encontrar estos métodos en la biblioteca SciJava Common :