Untuk mendapatkan File
untuk diberikan Class
, ada dua langkah:
- Konversi
Class
ke aURL
- Konversi
URL
ke aFile
Penting untuk memahami kedua langkah, dan tidak mengacaukannya.
Setelah Anda memiliki File
, Anda dapat menelepon getParentFile
untuk mendapatkan folder yang berisi, jika itu yang Anda butuhkan.
Langkah 1: Class
keURL
Seperti dibahas dalam jawaban lain, ada dua cara utama untuk menemukan yang URL
relevan dengan a Class
.
URL url = Bar.class.getProtectionDomain().getCodeSource().getLocation();
URL url = Bar.class.getResource(Bar.class.getSimpleName() + ".class");
Keduanya memiliki pro dan kontra.
The getProtectionDomain
Pendekatan menghasilkan lokasi dasar kelas (misalnya, berisi file JAR). Namun, ada kemungkinan bahwa kebijakan keamanan Java runtime akan dilemparkan SecurityException
saat memanggil getProtectionDomain()
, jadi jika aplikasi Anda perlu dijalankan di berbagai lingkungan, yang terbaik adalah mengujinya di semua lingkungan tersebut.
The getResource
Pendekatan menghasilkan path sumber daya URL lengkap dari kelas, dari mana Anda akan perlu melakukan tambahan manipulasi string. Ini mungkin file:
jalan, tetapi juga bisa jar:file:
atau bahkan sesuatu yang lebih buruk seperti bundleresource://346.fwk2106232034:4/foo/Bar.class
ketika mengeksekusi dalam kerangka OSGi. Sebaliknya, getProtectionDomain
pendekatan menghasilkan file:
URL dengan benar bahkan dari dalam OSGi.
Perhatikan bahwa keduanya getResource("")
dan getResource(".")
gagal dalam pengujian saya, ketika kelas berada dalam file JAR; kedua doa dikembalikan nol. Jadi saya merekomendasikan doa # 2 yang ditunjukkan di atas sebagai gantinya, karena tampaknya lebih aman.
Langkah 2: URL
keFile
Either way, setelah Anda memiliki URL
, langkah selanjutnya adalah mengonversi ke a File
. Ini adalah tantangannya sendiri; lihat posting blog Kohsuke Kawaguchi tentang hal itu untuk detail lengkap, tetapi singkatnya, Anda dapat menggunakan new File(url.toURI())
selama URL benar-benar terbentuk dengan baik.
Terakhir, saya akan sangat tidak suka menggunakan URLDecoder
. Beberapa karakter URL, :
dan /
khususnya, bukan karakter yang disandikan dengan URL. Dari URLDecoder Javadoc:
Diasumsikan bahwa semua karakter dalam string yang disandikan adalah salah satu dari yang berikut: "a" hingga "z", "A" hingga "Z", "0" hingga "9", dan "-", "_", " . ", dan" * ". Karakter "%" diperbolehkan tetapi ditafsirkan sebagai awal dari urutan lolos khusus.
...
Ada dua cara yang memungkinkan dekoder ini dapat menangani string ilegal. Itu bisa meninggalkan karakter ilegal sendirian atau bisa melempar IllegalArgumentException. Pendekatan mana yang diambil oleh decoder diserahkan pada implementasi.
Dalam praktiknya, URLDecoder
umumnya tidak melempar IllegalArgumentException
seperti terancam di atas. Dan jika path file Anda memiliki ruang yang disandikan %20
, pendekatan ini mungkin berfungsi. Namun, jika jalur file Anda memiliki karakter non-alfamerik lain seperti +
Anda akan mengalami masalah dengan URLDecoder
mengatur jalur file Anda.
Kode kerja
Untuk mencapai langkah-langkah ini, Anda mungkin memiliki metode seperti berikut:
/**
* 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);
}
Anda dapat menemukan metode ini di perpustakaan umum SciJava :