Pour obtenir le File
pour une donnée Class
, il y a deux étapes:
- Convertissez le
Class
enURL
- Convertissez le
URL
enFile
Il est important de comprendre les deux étapes et de ne pas les confondre.
Une fois que vous avez le File
, vous pouvez appeler getParentFile
pour obtenir le dossier contenant, si c'est ce dont vous avez besoin.
Étape 1: Class
pourURL
Comme indiqué dans d'autres réponses, il existe deux façons principales de trouver un URL
élément pertinent pour a Class
.
URL url = Bar.class.getProtectionDomain().getCodeSource().getLocation();
URL url = Bar.class.getResource(Bar.class.getSimpleName() + ".class");
Les deux ont des avantages et des inconvénients.
L' getProtectionDomain
approche donne l'emplacement de base de la classe (par exemple, le fichier JAR contenant). Cependant, il est possible que la politique de sécurité du runtime Java se SecurityException
déclenche lors de l'appel getProtectionDomain()
, donc si votre application doit s'exécuter dans une variété d'environnements, il est préférable de tester dans chacun d'eux.
L' getResource
approche produit le chemin d'accès complet aux ressources URL de la classe, à partir de laquelle vous devrez effectuer une manipulation de chaîne supplémentaire. Il peut s'agir d'un file:
chemin, mais il peut également l'être jar:file:
ou même quelque chose de plus méchant comme bundleresource://346.fwk2106232034:4/foo/Bar.class
lors de l'exécution dans un cadre OSGi. Inversement, l' getProtectionDomain
approche produit correctement une file:
URL même depuis OSGi.
Notez que les deux getResource("")
et ont getResource(".")
échoué dans mes tests, lorsque la classe résidait dans un fichier JAR; les deux invocations ont retourné null. Je recommande donc plutôt l'invocation n ° 2 ci-dessus, car elle semble plus sûre.
Étape 2: URL
pourFile
Quoi qu'il en soit, une fois que vous avez un URL
, l'étape suivante est convertie en a File
. C'est son propre défi; voir le blog de Kohsuke Kawaguchi à ce sujet pour plus de détails, mais en bref, vous pouvez l'utiliser new File(url.toURI())
tant que l'URL est complètement bien formée.
Enfin, je découragerais fortement l' utilisation URLDecoder
. Certains caractères de l'URL, :
et /
en particulier, ne sont pas des caractères codés URL valides. Depuis l' URLDecoder Javadoc:
Il est supposé que tous les caractères de la chaîne codée sont l'un des suivants: "a" à "z", "A" à "Z", "0" à "9" et "-", "_", " .", et "*". Le caractère "%" est autorisé mais est interprété comme le début d'une séquence d'échappement spéciale.
...
Il existe deux façons possibles pour ce décodeur de traiter les chaînes illégales. Il peut soit laisser les caractères illégaux seuls, soit lever une exception IllegalArgumentException. L'approche adoptée par le décodeur est laissée à la mise en œuvre.
En pratique, URLDecoder
ne jette généralement pas IllegalArgumentException
comme menacé ci-dessus. Et si votre chemin d'accès au fichier comporte des espaces codés en tant que %20
, cette approche peut sembler fonctionner. Cependant, si votre chemin d'accès au fichier contient d'autres caractères non alphamériques tels que +
vous aurez des problèmes avec la URLDecoder
modification de votre chemin d'accès au fichier.
Code de travail
Pour réaliser ces étapes, vous pouvez avoir des méthodes comme les suivantes:
/**
* 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);
}
Vous pouvez trouver ces méthodes dans la bibliothèque commune de SciJava :