2012-04-25 11 views
33

Quiero copiar un archivo de un contenedor. El archivo que estoy copiando se va a copiar fuera del directorio de trabajo. He hecho algunas pruebas y todos los métodos que intento terminan con archivos de 0 bytes.¿Cómo copiar el archivo dentro de jar al exterior del contenedor?

EDIT: Quiero que la copia del archivo se realice mediante un programa, no manualmente.

+2

Muéstranos lo que intentó. –

+0

¿Tienes que hacer esto programáticamente? Si no lo hace, puede cambiar el nombre de su 'Foo.jar' a' Foo.zip' y descomprimirlo normalmente. – Jeffrey

+0

@Jeffrey Sí, lo hago – jtl999

Respuesta

41

Antes que nada quiero decir que algunas de las respuestas publicadas anteriormente son completamente correctas, pero quiero dar las mías, ya que a veces no podemos usar bibliotecas de código abierto bajo la GPL, o porque somos demasiado perezosos para descargar el jar XD o cualquiera que sea su razón aquí es una solución independiente.

Ésta función copiar el recurso al lado del archivo JAR:

/** 
    * Export a resource embedded into a Jar file to the local file path. 
    * 
    * @param resourceName ie.: "/SmartLibrary.dll" 
    * @return The path to the exported resource 
    * @throws Exception 
    */ 
    static public String ExportResource(String resourceName) throws Exception { 
     InputStream stream = null; 
     OutputStream resStreamOut = null; 
     String jarFolder; 
     try { 
      stream = ExecutingClass.class.getResourceAsStream(resourceName);//note that each/is a directory down in the "jar tree" been the jar the root of the tree 
      if(stream == null) { 
       throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file."); 
      } 

      int readBytes; 
      byte[] buffer = new byte[4096]; 
      jarFolder = new File(ExecutingClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile().getPath().replace('\\', '/'); 
      resStreamOut = new FileOutputStream(jarFolder + resourceName); 
      while ((readBytes = stream.read(buffer)) > 0) { 
       resStreamOut.write(buffer, 0, readBytes); 
      } 
     } catch (Exception ex) { 
      throw ex; 
     } finally { 
      stream.close(); 
      resStreamOut.close(); 
     } 

     return jarFolder + resourceName; 
    } 

Sólo cambia ExecutingClass al nombre de su clase, y lo llaman así:

String fullPath = ExportResource("/myresource.ext"); 
+1

Lo curioso es que tuve que hacer esta tarea exacta para otro programa en el que estaba trabajando. Esto funciona. ¡Gracias! – jtl999

+0

¡Bienvenido, me alegra poder ayudarlo! – Ordiel

+1

Gracias Ordiel, pero ¿por qué establecemos el buffer szie como 4 bytes? – hackjutsu

0

Un jar es solo un archivo zip. Descomprímalo (usando el método con el que se sienta cómodo) y copie el archivo normalmente.

-1
${JAVA_HOME}/bin/jar -cvf /path/to.jar 
26

Dada su comentario acerca de los archivos de 0 bytes, tengo que asumir que estamos tratando de hacer esto mediante programación, y, dadas sus etiquetas, que lo está haciendo en Java. Si eso es cierto, simplemente use Class.getResource() para obtener una URL que apunte al archivo en su JAR, luego Apache Commons IO FileUtils.copyURLToFile() para copiarlo al sistema de archivos. Ej .:

URL inputUrl = getClass().getResource("/absolute/path/of/source/in/jar/file"); 
File dest = new File("/path/to/destination/file"); 
FileUtils.copyURLToFile(inputUrl, dest); 

Lo más probable, el problema con cualquier código que tiene ahora es que eres (correctamente) utilizando una corriente de salida con buffer para escribir en el archivo, pero (incorrectamente) al no cerrarla.

Ah, y habrá que editar su pregunta para aclarar exactamente cómo desea hacerlo (mediante programación, no, idioma, ...)

+0

Esta respuesta * asume * que el archivo JAR está en classpath. Este puede no ser el caso. E incluso si es el caso, puede haber otro archivo JAR en el classpath que oculte el archivo específico que el OP intenta extraer. –

+0

Cierto, pero si mis suposiciones son correctas, este me parece el escenario más probable. Si me equivoco, hay otras respuestas aquí que se encargarán de eso. –

+0

Buena interpretación de la pregunta antes de la edición. –

1

Utilice la clase JarInputStream:

// assuming you already have an InputStream to the jar file.. 
JarInputStream jis = new JarInputStream(is); 
// get the first entry 
JarEntry entry = jis.getNextEntry(); 
// we will loop through all the entries in the jar file 
while (entry != null) { 
    // test the entry.getName() against whatever you are looking for, etc 
    if (matches) { 
    // read from the JarInputStream until the read method returns -1 
    // ... 
    // do what ever you want with the read output 
    // ... 
    // if you only care about one file, break here 
    } 
    // get the next entry 
    entry = jis.getNextEntry(); 
} 
jis.close(); 

Ver también: JarEntry

9

Java 8 (en realidad FileSystem está allí desde 1.7) viene con algunas nuevas clases/métodos para lidiar con esto.Como alguien ya mencionó que JAR es básicamente archivo zip, puede utilizar

final URI jarFileUril = URI.create("jar:file:" + file.toURI().getPath()); 
final FileSystem fs = FileSystems.newFileSystem(jarFileUri, env); 

(Ver Zip File)

continuación, puede utilizar uno de los métodos convenientes como:

fs.getPath("filename"); 

A continuación, puede usar Archivos clase

try (final Stream<Path> sources = Files.walk(from)) { 
    sources.forEach(src -> { 
     final Path dest = to.resolve(from.relativize(src).toString()); 
     try { 
      if (Files.isDirectory(from)) { 
       if (Files.notExists(to)) { 
        log.trace("Creating directory {}", to); 
        Files.createDirectories(to); 
       } 
      } else { 
       log.trace("Extracting file {} to {}", from, to); 
       Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); 
      } 
     } catch (IOException e) { 
      throw new RuntimeException("Failed to unzip file.", e); 
     } 
    }); 
} 

Nota: I tr IED que para desempaquetar los archivos JAR para probar

+0

Muy interesante, analizaremos esto para futuros proyectos :) – jtl999

+0

Lo agregué aquí, solo para todos los que puedan toparse con esto y por qué no utilizar un enfoque nuevo/más corto. – Lukino

+0

'" jar: file: "+ file.toURI(). GetPath()' parece algo sin sentido. ¿Por qué no simplemente '" jar: "+ file.toURI()' ... por cierto, es 'toURI()' en lugar de 'toUri()' – Holger

0

Para copiar un archivo desde su frasco, hacia el exterior, es necesario utilizar el siguiente enfoque:

  1. Obtener una InputStream a un fichero dentro de su archivo JAR utilizando getResourceAsStream()
  2. abrimos nuestro archivo de destino utilizando un FileOutputStream
  3. copiamos bytes desde la entrada a la corriente de salida
  4. cerramos los arroyos para evitar pérdidas de recursos
código

Ejemplo que también contiene una variable para no sustituir los valores existentes:

public File saveResource(String name) throws IOException { 
    return saveResource(name, true); 
} 

public File saveResource(String name, boolean replace) throws IOException { 
    return saveResource(new File("."), name, replace) 
} 

public File saveResource(File outputDirectory, String name) throws IOException { 
    return saveResource(outputDirectory, name, true); 
} 

public File saveResource(File outputDirectory, String name, boolean replace) 
     throws IOException { 
    File out = new File(outputDirectory, name); 
    if (!replace && out.exists()) 
     return out; 
    // Step 1: 
    InputStream resource = this.getClass().getResourceAsStream(name); 
    if (resource == null) 
     throw new FileNotFoundException(name + " (resource not found)"); 
    // Step 2 and automatic step 4 
    try(InputStream in = resource; 
     OutputStream writer = new BufferedOutputStream(
      new FileOutputStream(out))) { 
     // Step 3 
     byte[] buffer = new byte[1024 * 4]; 
     int length; 
     while((length = in.read(buffer)) >= 0) { 
      writer.write(buffer, 0, length); 
     } 
    } 
    return out; 
} 
4

más rápida manera de hacerlo con Java 7+, además de código para obtener el directorio actual:

/** 
    * Copy a file from source to destination. 
    * 
    * @param source 
    *  the source 
    * @param destination 
    *  the destination 
    * @return True if succeeded , False if not 
    */ 
    public static boolean copy(InputStream source , String destination) { 
     boolean succeess = true; 

     System.out.println("Copying ->" + source + "\n\tto ->" + destination); 

     try { 
      Files.copy(source, Paths.get(destination), StandardCopyOption.REPLACE_EXISTING); 
     } catch (IOException ex) { 
      logger.log(Level.WARNING, "", ex); 
      succeess = false; 
     } 

     return succeess; 

    } 

Probándolo (icon.png es una imagen dentro de la imagen del paquete de la aplicación):

copy(getClass().getResourceAsStream("/image/icon.png"),getBasePathForClass(Main.class)+"icon.png"); 

Sobre la línea de código (getBasePathForClass(Main.class)): -> comprobar la respuesta que he añadido aquí :) ->Getting the Current Working Directory in Java

Cuestiones relacionadas