java - español - ¿Cómo copiar el archivo dentro de jar al exterior del jar?
que es un archivo jar (8)
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.
EDITAR : Quiero que la copia del archivo se realice a través de un programa, no manualmente.
Dado su comentario sobre los archivos de 0 bytes, tengo que suponer que está tratando de hacer esto programáticamente 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 en el sistema de archivos. P.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 es que el problema con el código que tenga ahora sea que está (correctamente) usando una secuencia de salida almacenada en el búfer para escribir en el archivo pero (incorrectamente) no cerrándola.
Ah, y deberías editar tu pregunta para aclarar exactamente cómo quieres hacer esto (programáticamente, no, idioma, ...)
En primer lugar, quiero decir que algunas 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 archivo jar XD o qué Alguna vez tu razón está aquí es una solución independiente.
La función siguiente copia 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;
}
Simplemente cambie ExecutingClass por el nombre de su clase y llámela así:
String fullPath = ExportResource("/myresource.ext");
Editar para Java 7+ (para su conveniencia)
Como respondió GOXR3PLUS y notado por Andy Thomas , puedes lograrlo con:
Files.copy( InputStream in, Path target, CopyOption... options)
Vea la respuesta de GOXR3PLUS para más detalles
Forma más rápida de hacerlo con Java 7+ , además del 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");
Acerca de la línea de código ( getBasePathForClass(Main.class)
): -> compruebe la respuesta que he agregado aquí :) -> Obteniendo el directorio de trabajo actual en Java
Java 8 (en realidad FileSystem está allí desde 1.7) viene con algunas clases / métodos nuevos y geniales para lidiar con esto. Como alguien ya mencionó que JAR es básicamente un archivo ZIP, podrías usar
final URI jarFileUril = URI.create("jar:file:" + file.toURI().getPath());
final FileSystem fs = FileSystems.newFileSystem(jarFileUri, env);
(Ver archivo zip )
Entonces puede usar uno de los métodos convenientes como:
fs.getPath("filename");
Entonces puedes usar la clase Files
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: Intenté eso para descomprimir archivos JAR para probar
Para copiar un archivo de su jar, al exterior, debe usar el siguiente enfoque:
- Obtenga un
InputStream
en el archivo dentro de su archivo jar usandogetResourceAsStream()
- Abrimos nuestro archivo de destino usando un
FileOutputStream
- Copiamos bytes de la entrada a la secuencia de salida
- Cerramos nuestras transmisiones para evitar fugas de recursos
Código de ejemplo que también contiene una variable para no reemplazar 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;
}
Un jar es solo un archivo zip. Descomprímalo (usando cualquier método con el que se sienta cómodo) y copie el archivo normalmente.
Use 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
${JAVA_HOME}/bin/jar -cvf /path/to.jar