usar programa hacer ejecutar ejecutable desde crear como archivos archivo aplicacion abrir java file jar java-io getresource

java - programa - ejecutar jar desde cmd



¿Cómo hago una lista de los archivos dentro de un archivo JAR? (13)

Así que supongo que mi problema principal sería saber cómo se llama el frasco donde vive mi clase principal.

Suponiendo que su proyecto está empaquetado en un Jar (¡no necesariamente cierto!), Puede usar ClassLoader.getResource () o findResource () con el nombre de clase (seguido de .class) para obtener el jar que contiene una clase determinada. Tendrás que analizar el nombre del jar desde la URL que se devuelve (no tan difícil), que dejaré como ejercicio para el lector :-)

Asegúrese de probar el caso en el que la clase no forma parte de un contenedor.

Tengo este código que lee todos los archivos de un directorio.

File textFolder = new File("text_directory"); File [] texFiles = textFolder.listFiles( new FileFilter() { public boolean accept( File file ) { return file.getName().endsWith(".txt"); } });

Funciona muy bien. Llena la matriz con todos los archivos que terminan con ".txt" del directorio "text_directory".

¿Cómo puedo leer el contenido de un directorio de manera similar dentro de un archivo JAR?

Entonces, lo que realmente quiero hacer es enumerar todas las imágenes dentro de mi archivo JAR, así puedo cargarlas con:

ImageIO.read(this.getClass().getResource("CompanyLogo.png"));

(Eso funciona porque el "CompanyLogo" está "codificado" pero el número de imágenes dentro del archivo JAR puede ser de 10 a 200 de longitud variable).

EDITAR

Así que supongo que mi problema principal sería: ¿Cómo saber el nombre del archivo JAR donde vive mi clase principal?

De acuerdo, podría leerlo usando java.util.Zip .

Mi estructura es así:

Ellos son como:

my.jar!/Main.class my.jar!/Aux.class my.jar!/Other.class my.jar!/images/image01.png my.jar!/images/image02a.png my.jar!/images/imwge034.png my.jar!/images/imagAe01q.png my.jar!/META-INF/manifest

Ahora mismo puedo cargar, por ejemplo, "images / image01.png" usando:

ImageIO.read(this.getClass().getResource("images/image01.png));

Pero solo porque sé el nombre del archivo, para el resto tengo que cargarlos dinámicamente.


Aquí hay un ejemplo del uso de la biblioteca Reflections para explorar recursivamente classpath por el patrón de nombre regex aumentado con un par de ventajas de Guava para recuperar los contenidos de los recursos:

Reflections reflections = new Reflections("com.example.package", new ResourcesScanner()); Set<String> paths = reflections.getResources(Pattern.compile(".*//.template$")); Map<String, String> templates = new LinkedHashMap<>(); for (String path : paths) { log.info("Found " + path); String templateName = Files.getNameWithoutExtension(path); URL resource = getClass().getClassLoader().getResource(path); String text = Resources.toString(resource, StandardCharsets.UTF_8); templates.put(templateName, text); }

Esto funciona tanto con jarras como con clases explosionadas.


Código que funciona para los archivos IDE y .jar:

import java.io.*; import java.net.*; import java.nio.file.*; import java.util.*; import java.util.stream.*; public class ResourceWalker { public static void main(String[] args) throws URISyntaxException, IOException { URI uri = ResourceWalker.class.getResource("/resources").toURI(); Path myPath; if (uri.getScheme().equals("jar")) { FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap()); myPath = fileSystem.getPath("/resources"); } else { myPath = Paths.get(uri); } Stream<Path> walk = Files.walk(myPath, 1); for (Iterator<Path> it = walk.iterator(); it.hasNext();){ System.out.println(it.next()); } } }


Dado un archivo JAR real, puede listar los contenidos usando JarFile.entries() . Sin embargo, necesitará saber la ubicación del archivo JAR; no puede simplemente pedirle al cargador de clases que enumere todo lo que podría obtener.

Debería poder calcular la ubicación del archivo JAR según la URL devuelta por ThisClassName.class.getResource("ThisClassName.class") , pero puede ser un poco complicado.


Este es un método que escribí para "ejecutar todos los JUnits bajo un paquete". Debería poder adaptarlo a sus necesidades.

private static void findClassesInJar(List<String> classFiles, String path) throws IOException { final String[] parts = path.split("//Q.jar////E"); if (parts.length == 2) { String jarFilename = parts[0] + ".jar"; String relativePath = parts[1].replace(File.separatorChar, ''/''); JarFile jarFile = new JarFile(jarFilename); final Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { final JarEntry entry = entries.nextElement(); final String entryName = entry.getName(); if (entryName.startsWith(relativePath)) { classFiles.add(entryName.replace(''/'', File.separatorChar)); } } } }

Editar: Ah, en ese caso, es posible que desee este fragmento también (el mismo caso de uso :))

private static File findClassesDir(Class<?> clazz) { try { String path = clazz.getProtectionDomain().getCodeSource().getLocation().getFile(); final String codeSourcePath = URLDecoder.decode(path, "UTF-8"); final String thisClassPath = new File(codeSourcePath, clazz.getPackage().getName().repalce(''.'', File.separatorChar)); } catch (UnsupportedEncodingException e) { throw new AssertionError("impossible", e); } }


Hace algún tiempo hice una función que obtiene classess desde dentro de JAR:

public static Class[] getClasses(String packageName) throws ClassNotFoundException{ ArrayList<Class> classes = new ArrayList<Class> (); packageName = packageName.replaceAll("//." , "/"); File f = new File(jarName); if(f.exists()){ try{ JarInputStream jarFile = new JarInputStream( new FileInputStream (jarName)); JarEntry jarEntry; while(true) { jarEntry=jarFile.getNextJarEntry (); if(jarEntry == null){ break; } if((jarEntry.getName ().startsWith (packageName)) && (jarEntry.getName ().endsWith (".class")) ) { classes.add(Class.forName(jarEntry.getName(). replaceAll("/", "//."). substring(0, jarEntry.getName().length() - 6))); } } } catch( Exception e){ e.printStackTrace (); } Class[] classesA = new Class[classes.size()]; classes.toArray(classesA); return classesA; }else return null; }



He portado answer a Java 7 y he cerrado el objeto FileSystem . Este código funciona en IDE, en archivos jar y en un jar dentro de una guerra en Tomcat 7; pero tenga en cuenta que no funciona en un contenedor dentro de una guerra en JBoss 7 (da FileSystemNotFoundException: Provider "vfs" not installed , consulte también esta publicación ). Además, al igual que el código original, no es seguro para subprocesos, como sugiere errr . Por estas razones, he abandonado esta solución; Sin embargo, si puede aceptar estos problemas, aquí está mi código listo:

import java.io.IOException; import java.net.*; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; public class ResourceWalker { public static void main(String[] args) throws URISyntaxException, IOException { URI uri = ResourceWalker.class.getResource("/resources").toURI(); System.out.println("Starting from: " + uri); try (FileSystem fileSystem = (uri.getScheme().equals("jar") ? FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap()) : null)) { Path myPath = Paths.get(uri); Files.walkFileTree(myPath, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println(file); return FileVisitResult.CONTINUE; } }); } } }


La answer de Erickson funcionó a la perfección:

Aquí está el código de trabajo.

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource(); List<String> list = new ArrayList<String>(); if( src != null ) { URL jar = src.getLocation(); ZipInputStream zip = new ZipInputStream( jar.openStream()); ZipEntry ze = null; while( ( ze = zip.getNextEntry() ) != null ) { String entryName = ze.getName(); if( entryName.startsWith("images") && entryName.endsWith(".png") ) { list.add( entryName ); } } } webimages = list.toArray( new String[ list.size() ] );

Y acabo de modificar mi método de carga a partir de esto:

File[] webimages = ... BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex].getName() ));

A esto:

String [] webimages = ... BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));


Me gustaría ampliar la answer de acheron55, ya que es una solución muy segura, por varias razones:

  1. No cierra el objeto FileSystem .
  2. No comprueba si el objeto FileSystem ya existe.
  3. No es seguro para subprocesos.

Esta es de alguna manera una solución más segura:

private static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>(); public void walk(String path) throws Exception { URI uri = getClass().getResource(path).toURI(); if ("jar".equals(uri.getScheme()) { safeWalkJar(path, uri); } else { Files.walk(Paths.get(path)); } } private void safeWalkJar(String path, URI uri) throws Exception { synchronized (getLock(uri)) { // this''ll close the FileSystem object at the end try (FileSystem fs = getFileSystem(uri)) { Files.walk(fs.getPath(path)); } } } private Object getLock(URI uri) { String fileName = parseFileName(uri); locks.computeIfAbsent(fileName, s -> new Object()); return locks.get(fileName); } private String parseFileName(URI uri) { String schemeSpecificPart = uri.getSchemeSpecificPart(); return schemeSpecificPart.substring(0, schemeSpecificPart.indexOf("!")); } private FileSystem getFileSystem(URI uri) throws IOException { try { return FileSystems.getFileSystem(uri); } catch (FileSystemNotFoundException e) { return FileSystems.newFileSystem(uri, Collections.<String, String>emptyMap()); } }

No es necesario sincronizar sobre el nombre del archivo; uno simplemente podría sincronizarse en el mismo objeto cada vez (o synchronized el método), es puramente una optimización.

Diría que esta sigue siendo una solución problemática, ya que puede haber otras partes en el código que utilicen la interfaz FileSystem en los mismos archivos, y podría interferir con ellas (incluso en una única aplicación con subprocesos).
Además, no verifica null s (por ejemplo, en getClass().getResource() .

Esta particular interfaz Java NIO es algo horrible, ya que presenta un recurso global / singleton no seguro para subprocesos, y su documentación es extremadamente vaga (muchas incógnitas debido a implementaciones específicas del proveedor). Los resultados pueden variar para otros proveedores de FileSystem (no JAR). Tal vez haya una buena razón para que sea de esa manera; No sé, no he investigado las implementaciones.


Solo una forma diferente de listar / leer archivos de una URL jar y lo hace recursivamente para jarras anidadas

https://gist.github.com/trung/2cd90faab7f75b3bcbaa

URL urlResource = Thead.currentThread().getContextClassLoader().getResource("foo"); JarReader.read(urlResource, new InputStreamCallback() { @Override public void onFile(String name, InputStream is) throws IOException { // got file name and content stream } });


Un archivo jar es solo un archivo zip con un manifiesto estructurado. Puede abrir el archivo jar con las herramientas java zip habituales y escanear el contenido del archivo de esa manera, inflar las secuencias, etc. Luego, use eso en una llamada getResourceAsStream, y debería ser todo hunky dory.

EDITAR / después de la aclaración

Me tomó un minuto recordar todas las partes y estoy seguro de que hay maneras más limpias de hacerlo, pero quería ver que no estaba loca. En mi proyecto image.jpg es un archivo en alguna parte del archivo jar principal. Obtengo el cargador de clase de la clase principal (SomeClass es el punto de entrada) y lo uso para descubrir el recurso image.jpg. Luego, algo de magia de transmisión para meterlo en esta cosa de ImageInputStream y todo está bien.

InputStream inputStream = SomeClass.class.getClassLoader().getResourceAsStream("image.jpg"); JPEGImageReaderSpi imageReaderSpi = new JPEGImageReaderSpi(); ImageReader ir = imageReaderSpi.createReaderInstance(); ImageInputStream iis = new MemoryCacheImageInputStream(inputStream); ir.setInput(iis); .... ir.read(0); //will hand us a buffered image


CodeSource src = MyClass.class.getProtectionDomain().getCodeSource(); if (src != null) { URL jar = src.getLocation(); ZipInputStream zip = new ZipInputStream(jar.openStream()); while(true) { ZipEntry e = zip.getNextEntry(); if (e == null) break; String name = e.getName(); if (name.startsWith("path/to/your/dir/")) { /* Do something with this entry. */ ... } } } else { /* Fail... */ }

Tenga en cuenta que en Java 7, puede crear un FileSystem partir del archivo JAR (zip) y luego utilizar los mecanismos de búsqueda y filtrado de directorio de NIO para buscar a través de él. Esto haría más fácil escribir código que maneje JAR y directorios "explotados".