Per ottenere il File
per un dato Class
, ci sono due passaggi:
- Converti
Class
in aURL
- Converti
URL
in aFile
È importante comprendere entrambi i passaggi e non confonderli.
Una volta che hai il File
, puoi chiamaregetParentFile
per ottenere la cartella contenente, se è quello che ti serve.
Passaggio 1: Class
aURL
Come discusso in altre risposte, ci sono due modi principali per trovare un URL
rilevante per a Class
.
URL url = Bar.class.getProtectionDomain().getCodeSource().getLocation();
URL url = Bar.class.getResource(Bar.class.getSimpleName() + ".class");
Entrambi hanno pro e contro.
L' getProtectionDomain
approccio fornisce la posizione di base della classe (ad esempio, il file JAR contenente). Tuttavia, è possibile che la politica di sicurezza del runtime Java venga lanciata SecurityException
durante la chiamata getProtectionDomain()
, quindi se l'applicazione deve essere eseguita in una varietà di ambienti, è meglio testarli in tutti.
L' getResource
approccio fornisce il percorso completo delle risorse URL della classe, da cui sarà necessario eseguire un'ulteriore manipolazione delle stringhe. Potrebbe essere un file:
percorso, ma potrebbe anche essere jar:file:
o addirittura qualcosa di più cattivo come bundleresource://346.fwk2106232034:4/foo/Bar.class
quando si esegue all'interno di un framework OSGi. Al contrario, l' getProtectionDomain
approccio produce correttamente afile:
URL anche all'interno di OSGi.
Nota che entrambi getResource("")
e getResource(".")
fallito nei miei test, quando la classe risiedeva in un file JAR; entrambe le invocazioni hanno restituito null. Quindi raccomando invece l'invocazione n. 2 mostrata sopra, poiché sembra più sicura.
Passaggio 2: URL
aFile
Ad ogni modo, una volta che hai un URL
, il passaggio successivo è convertire in un File
. Questa è la sua sfida; vedi il post sul blog di Kohsuke Kawaguchi per maggiori dettagli, ma in breve puoi usarlonew File(url.toURI())
fino a quando l'URL è completamente ben formato.
Infine, scoraggerei fortemente l' utilizzo URLDecoder
. Alcuni caratteri dell'URL, :
e /
in particolare, non sono caratteri con codifica URL validi. Da URLDecoder Javadoc:
Si presume che tutti i caratteri nella stringa codificata siano uno dei seguenti: "da" a "z", da "A" a "Z", da "0" a "9" e "-", "_", " .", e "*". Il carattere "%" è consentito ma viene interpretato come l'inizio di una sequenza con escape speciale.
...
Esistono due modi in cui questo decodificatore potrebbe gestire stringhe illegali. Potrebbe lasciare da solo i personaggi illegali o lanciare un'eccezione IllegalArgumentException. L'approccio adottato dal decodificatore è lasciato all'implementazione.
In pratica, URLDecoder
generalmente non getta IllegalArgumentException
come sopra minacciato. E se il percorso del file ha spazi codificati come %20
, questo approccio potrebbe sembrare funzionare. Tuttavia, se il percorso del file contiene altri caratteri non alfanumerici, ad esempio, +
si avranno problemi con URLDecoder
la modifica del percorso del file.
Codice di lavoro
Per ottenere questi passaggi, potresti avere metodi come il seguente:
/**
* 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);
}
Puoi trovare questi metodi nella libreria SciJava Common :