java - ¿Cómo obtener la ruta de un archivo JAR en ejecución?
path executable-jar (29)
Mi código se ejecuta dentro de un archivo JAR, digamos foo.jar, y necesito saber, en el código, en qué carpeta está ejecutando foo.jar.
Entonces, si foo.jar está en C:/FOO/
, quiero obtener esa ruta sin importar cuál sea mi directorio de trabajo actual.
Algo frustrante es que cuando se está desarrollando en Eclipse MyClass.class.getProtectionDomain().getCodeSource().getLocation()
devuelve el directorio /bin
, lo cual es genial, pero cuando lo compila en un jar, la ruta incluye el /myjarname.jar
que le da nombres de archivos ilegales.
Para que el código funcione tanto en el ide como en el momento de compilarlo en un contenedor, utilizo el siguiente código:
URL applicationRootPathURL = getClass().getProtectionDomain().getCodeSource().getLocation();
File applicationRootPath = new File(applicationRootPathURL.getPath());
File myFile;
if(applicationRootPath.isDirectory()){
myFile = new File(applicationRootPath, "filename");
}
else{
myFile = new File(applicationRootPath.getParentFile(), "filename");
}
Aquí está la actualización a otros comentarios, que me parecen incompletos para los detalles de
usando una "carpeta" relativa fuera del archivo .jar (en la misma ubicación del contenedor):
String path =
YourMainClassName.class.getProtectionDomain().
getCodeSource().getLocation().getPath();
path =
URLDecoder.decode(
path,
"UTF-8");
BufferedImage img =
ImageIO.read(
new File((
new File(path).getParentFile().getPath()) +
File.separator +
"folder" +
File.separator +
"yourfile.jpg"));
El enfoque de getProtectionDomain
puede no funcionar a veces, por ejemplo, cuando tiene que encontrar el jar para algunas de las clases java principales (por ejemplo, en mi caso, la clase StringBuilder
en IBM JDK), sin embargo, el siguiente trabajo funciona a la perfección:
public static void main(String[] args) {
System.out.println(findSource(MyClass.class));
// OR
System.out.println(findSource(String.class));
}
public static String findSource(Class<?> clazz) {
String resourceToSearch = ''/'' + clazz.getName().replace(".", "/") + ".class";
java.net.URL location = clazz.getResource(resourceToSearch);
String sourcePath = location.getPath();
// Optional, Remove junk
return sourcePath.replace("file:", "").replace("!" + resourceToSearch, "");
}
En realidad, aquí hay una versión mejor: la anterior fallaba si el nombre de una carpeta tenía un espacio en ella.
private String getJarFolder() {
// get name and path
String name = getClass().getName().replace(''.'', ''/'');
name = getClass().getResource("/" + name + ".class").toString();
// remove junk
name = name.substring(0, name.indexOf(".jar"));
name = name.substring(name.lastIndexOf('':'')-1, name.lastIndexOf(''/'')+1).replace(''%'', '' '');
// remove escape characters
String s = "";
for (int k=0; k<name.length(); k++) {
s += name.charAt(k);
if (name.charAt(k) == '' '') k += 2;
}
// replace ''/'' with system separator char
return s.replace(''/'', File.separatorChar);
}
En cuanto a fallar con los applets, normalmente no tendrías acceso a los archivos locales de todos modos. No sé mucho sobre JWS, pero para manejar archivos locales podría no ser posible descargar la aplicación.
Escribo en Java 7 y pruebo en Windows 7 con el tiempo de ejecución de Oracle, y Ubuntu con el tiempo de ejecución de código abierto. Esto funciona perfecto para esos sistemas:
La ruta del directorio principal de cualquier archivo jar en ejecución (asumiendo que la clase que llama a este código es un hijo directo del propio archivo jar):
try {
fooDir = new File(this.getClass().getClassLoader().getResource("").toURI());
} catch (URISyntaxException e) {
//may be sloppy, but don''t really need anything here
}
fooDirPath = fooDir.toString(); // converts abstract (absolute) path to a String
Entonces, el camino de foo.jar sería:
fooPath = fooDirPath + File.separator + "foo.jar";
Nuevamente, esto no fue probado en ninguna Mac o Windows anterior
Este código funcionó para mí:
private static String getJarPath() throws IOException, URISyntaxException {
File f = new File(LicensingApp.class.getProtectionDomain().().getLocation().toURI());
String jarPath = f.getCanonicalPath().toString();
String jarDir = jarPath.substring( 0, jarPath.lastIndexOf( File.separator ));
return jarDir;
}
Este método, llamado desde el código en el archivo, devuelve la carpeta donde está el archivo .jar. Debería funcionar en Windows o Unix.
private String getJarFolder() {
String name = this.getClass().getName().replace(''.'', ''/'');
String s = this.getClass().getResource("/" + name + ".class").toString();
s = s.replace(''/'', File.separatorChar);
s = s.substring(0, s.indexOf(".jar")+4);
s = s.substring(s.lastIndexOf('':'')-1);
return s.substring(0, s.lastIndexOf(File.separatorChar)+1);
}
Derivado del código en: Determine si se ejecuta desde JAR
Este trazador de líneas funciona con carpetas que contienen espacios o caracteres especiales (como ç o õ). La pregunta original solicita la ruta absoluta (dir de trabajo), sin el archivo JAR en sí. Probado aquí con Java7 en Windows7:
String workingDir = System.getProperty("user.dir");
Referencia: http://www.mkyong.com/java/how-to-get-the-current-working-directory-in-java/
Ignorar la respuesta del chico de respaldo, puede verse bien a veces pero tiene varios problemas:
aquí ambos deberían ser +1 no -1:
name = name.substring(name.lastIndexOf('':'')-1, name.lastIndexOf(''/'')+1).replace(''%'', '' '');
Muy peligroso porque no es evidente de inmediato si la ruta no tiene espacios en blanco, pero reemplazar solo el "%" lo dejará con un grupo de 20 en cada espacio en blanco:
name = name.substring(name.lastIndexOf('':'')-1, name.lastIndexOf(''/'')+1).replace(''%'', '' '');
Hay mejores maneras que ese bucle para los espacios en blanco.
También causará problemas en el momento de la depuración.
La única solución que me funciona en Linux, Mac y Windows:
public static String getJarContainingFolder(Class aclass) throws Exception {
CodeSource codeSource = aclass.getProtectionDomain().getCodeSource();
File jarFile;
if (codeSource.getLocation() != null) {
jarFile = new File(codeSource.getLocation().toURI());
}
else {
String path = aclass.getResource(aclass.getSimpleName() + ".class").getPath();
String jarFilePath = path.substring(path.indexOf(":") + 1, path.indexOf("!"));
jarFilePath = URLDecoder.decode(jarFilePath, "UTF-8");
jarFile = new File(jarFilePath);
}
return jarFile.getParentFile().getAbsolutePath();
}
La mejor solución para mí:
String path = Test.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = URLDecoder.decode(path, "UTF-8");
Esto debería resolver el problema con espacios y caracteres especiales.
La solución más sencilla es pasar la ruta como un argumento cuando se ejecuta el archivo jar.
Puede automatizar esto con un script de shell (.bat en Windows, .sh en cualquier otro lugar):
java -jar my-jar.jar .
Yo utilicé para pasar el directorio de trabajo actual.
ACTUALIZAR
Es posible que desee pegar el archivo jar en un subdirectorio para que los usuarios no lo hagan clic accidentalmente. Su código también debe verificar para asegurarse de que se hayan proporcionado los argumentos de la línea de comandos y proporcionar un buen mensaje de error si faltan los argumentos.
Me sorprende ver que ninguno propuso recientemente usar Path
. Aquí sigue una cita: " La clase Path
incluye varios métodos que pueden usarse para obtener información sobre la ruta, acceder a los elementos de la ruta, convertir la ruta a otras formas o extraer partes de una ruta "
Por lo tanto, una buena alternativa es obtener el Objest del Path
como:
Path path = Paths.get(Test.class.getProtectionDomain().getCodeSource().getLocation().toURI());
No estoy realmente seguro de los demás, pero en mi caso no funcionó con un "tarro Runnable" y lo conseguí trabajando corrigiendo códigos juntos desde la respuesta phchen2 y otro desde este enlace: ¿Cómo obtener la ruta de un archivo JAR en ejecución? El código:
String path=new java.io.File(Server.class.getProtectionDomain()
.getCodeSource()
.getLocation()
.getPath())
.getAbsolutePath();
path=path.substring(0, path.lastIndexOf("."));
path=path+System.getProperty("java.class.path");
O puedes pasar el hilo actual de esta manera:
String myPath = Thread.currentThread().getContextClassLoader().getResource("filename").getPath();
Otras respuestas parecen apuntar a la fuente del código que es la ubicación del archivo Jar que no es un directorio.
Utilizar
return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile();
Para obtener el File
para una Class
dada, hay dos pasos:
- Convertir la
Class
a unaURL
- Convertir la
URL
en unFile
Es importante entender ambos pasos, y no confundirlos.
Una vez que tenga el File
, puede llamar a getParentFile
para obtener la carpeta que lo contiene, si eso es lo que necesita.
Paso 1: Class
a URL
Como se discutió en otras respuestas, hay dos formas principales de encontrar una URL
relevante para una Class
.
URL url = Bar.class.getProtectionDomain().getCodeSource().getLocation();
URL url = Bar.class.getResource(Bar.class.getSimpleName() + ".class");
Ambos tienen pros y contras.
El enfoque getProtectionDomain
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 arroje SecurityException
getProtectionDomain()
SecurityException
al llamar a getProtectionDomain()
, por lo que si su aplicación necesita ejecutarse en una variedad de entornos, es mejor probarla en todos ellos.
El enfoque getResource
proporciona la ruta completa de recursos de URL de la clase, desde la cual deberá realizar una manipulación de cadena adicional. Puede ser un file:
ruta, pero también puede ser jar:file:
o incluso algo más desagradable como bundleresource://346.fwk2106232034:4/foo/Bar.class
cuando se ejecuta dentro de un marco OSGi. A la inversa, el enfoque getProtectionDomain
produce correctamente un file:
URL incluso desde OSGi.
Tenga en cuenta que tanto getResource("")
como getResource(".")
Fallaron en mis pruebas, cuando la clase residía dentro de un archivo JAR; ambas invocaciones devuelven nulo. Así que recomiendo la invocación # 2 que se muestra arriba, ya que parece más segura.
Paso 2: URL
a File
De cualquier manera, una vez que tenga una URL
, el siguiente paso es convertir a un File
. Este es su propio reto; vea la publicación del blog de Kohsuke Kawaguchi al respecto para obtener todos los detalles, pero en resumen, puede usar el new File(url.toURI())
siempre que la URL esté completamente formada.
Por último, me gustaría mucho desalentar el uso de URLDecoder
. Algunos caracteres de la URL,: y /
en particular, no son válidos con codificación de URL. Desde el Javadoc URLDecoder :
Se supone que todos los caracteres de 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 las que este decodificador podría tratar con cadenas ilegales. Puede dejar los caracteres ilegales solos o lanzar una excepción IllegalArgumentException. El enfoque que toma el decodificador se deja a la implementación.
En la práctica, URLDecoder
generalmente no lanza la IllegalArgumentException
como se amenazó anteriormente. Y si la ruta del archivo tiene espacios codificados como %20
, este enfoque puede parecer que funciona. Sin embargo, si su ruta de archivo tiene otros caracteres no alfanaméricos, como +
, tendrá problemas con URLDecoder
su ruta de archivo.
Código de trabajo
Para lograr estos pasos, es posible que tenga 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);
}
Puedes encontrar estos métodos en la biblioteca común de SciJava :
Para obtener la ruta de ejecución del archivo jar, he estudiado las soluciones anteriores y he probado todos los métodos que existen y que difieren entre sí. Si estos códigos se ejecutan en Eclipse IDE, todos deberían poder encontrar la ruta del archivo, incluida la clase indicada, y abrir o crear un archivo indicado con la ruta encontrada.
Pero es complicado, cuando se ejecuta el archivo jar ejecutable directamente oa través de la línea de comandos, se producirá un error, ya que la ruta del archivo jar obtenida de los métodos anteriores proporcionará una ruta interna en el archivo jar, es decir, siempre proporciona una ruta como
rsrc: nombre-proyecto (tal vez debería decir que es el nombre del paquete del archivo de clase principal, la clase indicada)
No puedo convertir la ruta rsrc: ... a una ruta externa, es decir, cuando se ejecuta el archivo jar fuera del IDE de Eclipse, no se puede obtener la ruta del archivo jar.
La única forma posible de obtener la ruta de ejecución del archivo jar fuera del IDE de Eclipse es
System.getProperty("java.class.path")
esta línea de código puede devolver la ruta activa (incluido el nombre del archivo) del archivo jar en ejecución (tenga en cuenta que la ruta de retorno no es el directorio de trabajo), ya que el documento java y algunas personas dijeron que devolverá las rutas de todos los archivos de clase en el mismo directorio, pero como mis pruebas si en el mismo directorio incluyen muchos archivos jar, solo devuelve la ruta de ejecución de jar (sobre el problema de las rutas múltiples que ocurrió en el Eclipse).
También puedes usar:
CodeSource codeSource = YourMainClass.class.getProtectionDomain().getCodeSource();
File jarFile = new File(codeSource.getLocation().toURI().getPath());
String jarDir = jarFile.getParentFile().getPath();
Tengo otra forma de obtener la ubicación String de una clase.
URL path = Thread.currentThread().getContextClassLoader().getResource("");
Path p = Paths.get(path.toURI());
String location = p.toString();
La cadena de salida tendrá la forma de
C:/Users/Administrator/new Workspace/...
Los espacios y otros caracteres se manejan, y en la forma sin file:/
. Así será más fácil de usar.
Traté de hacer correr el tarro usando
String folder = MyClassName.class.getProtectionDomain().getCodeSource().getLocation().getPath();
c: / app> java -jar application.jar
Al ejecutar la aplicación jar llamada "application.jar", en Windows en la carpeta " c: / app ", el valor de la variable String "carpeta" era " / c: / app / application.jar " y tuve problemas en las pruebas la corrección del camino
File test = new File(folder);
if(file.isDirectory() && file.canRead()) { //always false }
Así que traté de definir "prueba" como:
String fold= new File(folder).getParentFile().getPath()
File test = new File(fold);
para obtener la ruta en un formato correcto como " c: / app " en lugar de " / c: / app / application.jar " y noté que funciona.
Tuve el mismo problema y lo resolví de esa manera:
File currentJavaJarFile = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().getPath());
String currentJavaJarFilePath = currentJavaJarFile.getAbsolutePath();
String currentRootDirectoryPath = currentJavaJarFilePath.replace(currentJavaJarFile.getName(), "");
Espero haberte ayudado.
Tuve que perder mucho tiempo antes de finalmente encontrar una solución de trabajo (y corta).
Es posible que jarLocation
un prefijo como file:/
o jar:file/
, que puede eliminarse utilizando String#substring()
.
URL jarLocationUrl = MyClass.class.getProtectionDomain().getCodeSource().getLocation();
String jarLocation = new File(jarLocationUrl.toString()).getParent();
Use ClassLoader.getResource () para encontrar la URL de su clase actual.
Por ejemplo:
package foo;
public class Test
{
public static void main(String[] args)
{
ClassLoader loader = Test.class.getClassLoader();
System.out.println(loader.getResource("foo/Test.class"));
}
}
(Este ejemplo está tomado de una pregunta similar .)
Para encontrar el directorio, deberá desarmar la URL manualmente. Consulte el tutorial de JarClassLoader para conocer el formato de una URL de jar.
la respuesta seleccionada anteriormente no funciona si ejecuta su archivo jar haciendo clic en él desde el entorno de escritorio Gnome (no desde cualquier script o terminal).
En cambio, me gusta que la siguiente solución funcione en todas partes:
try {
return URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}
Mencione que está comprobado solo en Windows
pero creo que funciona perfectamente en otros sistemas operativos [ Linux,MacOs,Solaris
] :).
Tenía 2 archivos .jar
en el mismo directorio. Quería que desde un archivo .jar
comenzara el otro archivo .jar
que está en el mismo directorio.
El problema es que cuando lo inicias desde el cmd
el directorio actual es system32
.
Advertencias!
- Lo siguiente parece funcionar bastante bien en todas las pruebas que he hecho, incluso con el nombre de la carpeta
;][[;''57f2g34g87-8+9-09!2#@!$%^^&()
o()%&$%^@#
funciona bien. - Estoy usando
ProcessBuilder
con lo siguiente como sigue:
🍂 ..
//The class from which i called this was the class `Main`
String path = getBasePathForClass(Main.class);
String applicationPath= new File(path + "application.jar").getAbsolutePath();
System.out.println("Directory Path is : "+applicationPath);
//Your know try catch here
//Mention that sometimes it doesn''t work for example with folder `;][[;''57f2g34g87-8+9-09!2#@!$%^^&()`
ProcessBuilder builder = new ProcessBuilder("java", "-jar", applicationPath);
builder.redirectErrorStream(true);
Process process = builder.start();
//...code
🍂 getBasePathForClass(Class<?> classs)
:
/**
* Returns the absolute path of the current directory in which the given
* class
* file is.
*
* @param classs
* @return The absolute path of the current directory in which the class
* file is.
* @author GOXR3PLUS[ user] + bachden [ user]
*/
public static final String getBasePathForClass(Class<?> classs) {
// Local variables
File file;
String basePath = "";
boolean failed = false;
// Let''s give a first try
try {
file = new File(classs.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
if (file.isFile() || file.getPath().endsWith(".jar") || file.getPath().endsWith(".zip")) {
basePath = file.getParent();
} else {
basePath = file.getPath();
}
} catch (URISyntaxException ex) {
failed = true;
Logger.getLogger(classs.getName()).log(Level.WARNING,
"Cannot firgue out base path for class with way (1): ", ex);
}
// The above failed?
if (failed) {
try {
file = new File(classs.getClassLoader().getResource("").toURI().getPath());
basePath = file.getAbsolutePath();
// the below is for testing purposes...
// starts with File.separator?
// String l = local.replaceFirst("[" + File.separator +
// "/////]", "")
} catch (URISyntaxException ex) {
Logger.getLogger(classs.getName()).log(Level.WARNING,
"Cannot firgue out base path for class with way (2): ", ex);
}
}
// fix to run inside eclipse
if (basePath.endsWith(File.separator + "lib") || basePath.endsWith(File.separator + "bin")
|| basePath.endsWith("bin" + File.separator) || basePath.endsWith("lib" + File.separator)) {
basePath = basePath.substring(0, basePath.length() - 4);
}
// fix to run inside netbeans
if (basePath.endsWith(File.separator + "build" + File.separator + "classes")) {
basePath = basePath.substring(0, basePath.length() - 14);
}
// end fix
if (!basePath.endsWith(File.separator)) {
basePath = basePath + File.separator;
}
return basePath;
}
String path = getClass().getResource("").getPath();
La ruta siempre se refiere al recurso dentro del archivo jar.
public static String dir() throws URISyntaxException
{
URI path=Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
String name= Main.class.getPackage().getName()+".jar";
String path2 = path.getRawPath();
path2=path2.substring(1);
if (path2.contains(".jar"))
{
path2=path2.replace(name, "");
}
return path2;}
Funciona bien en Windows
return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getPath();
Reemplaza "MyClass" con el nombre de tu clase
Obviamente, esto hará cosas extrañas si su clase se cargó desde una ubicación que no es un archivo.