Solución:
En Java 7 tenemos Zip File System que permite agregar y cambiar archivos en zip (jar, war) sin reempaquetarlos manualmente.
Podemos escribir directamente en archivos dentro de archivos zip como en el siguiente ejemplo.
Map<String, String> env = new HashMap<>();
env.put("create", "true");
Path path = Paths.get("test.zip");
URI uri = URI.create("jar:" + path.toUri());
try (FileSystem fs = FileSystems.newFileSystem(uri, env))
{
Path nf = fs.getPath("new.txt");
try (Writer writer = Files.newBufferedWriter(nf, StandardCharsets.UTF_8, StandardOpenOption.CREATE)) {
writer.write("hello");
}
}
Como otros mencionaron, no es posible agregar contenido a un zip (o guerra) existente. Sin embargo, es posible crear un nuevo zip sobre la marcha sin escribir temporalmente el contenido extraído en el disco. Es difícil adivinar cuánto más rápido será esto, pero es lo más rápido que puede obtener (al menos hasta donde yo sé) con Java estándar. Como lo mencionó Carlos Tasada, SevenZipJBindings podría exprimirle algunos segundos adicionales, pero trasladar este enfoque a SevenZipJBindings seguirá siendo más rápido que usar archivos temporales con la misma biblioteca.
Aquí hay un código que escribe el contenido de un zip existente (war.zip) y agrega un archivo adicional (answer.txt) a un nuevo zip (append.zip). Todo lo que necesita es Java 5 o posterior, no se necesitan bibliotecas adicionales.
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class Main {
// 4MB buffer
private static final byte[] BUFFER = new byte[4096 * 1024];
/**
* copy input to output stream - available in several StreamUtils or Streams classes
*/
public static void copy(InputStream input, OutputStream output) throws IOException {
int bytesRead;
while ((bytesRead = input.read(BUFFER))!= -1) {
output.write(BUFFER, 0, bytesRead);
}
}
public static void main(String[] args) throws Exception {
// read war.zip and write to append.zip
ZipFile war = new ZipFile("war.zip");
ZipOutputStream append = new ZipOutputStream(new FileOutputStream("append.zip"));
// first, copy contents from existing war
Enumeration<? extends ZipEntry> entries = war.entries();
while (entries.hasMoreElements()) {
ZipEntry e = entries.nextElement();
System.out.println("copy: " + e.getName());
append.putNextEntry(e);
if (!e.isDirectory()) {
copy(war.getInputStream(e), append);
}
append.closeEntry();
}
// now append some extra content
ZipEntry e = new ZipEntry("answer.txt");
System.out.println("append: " + e.getName());
append.putNextEntry(e);
append.write("42n".getBytes());
append.closeEntry();
// close
war.close();
append.close();
}
}
Hace algún tiempo tuve un requisito similar, pero era para leer y escribir archivos zip (el formato .war debería ser similar). Intenté hacerlo con las transmisiones Java Zip existentes, pero la parte de escritura me resultó engorrosa, especialmente cuando los directorios estaban involucrados.
Le recomendaré que pruebe la biblioteca TrueZIP (código abierto, con licencia de estilo apache) que expone cualquier archivo como un sistema de archivos virtual en el que puede leer y escribir como un sistema de archivos normal. Funcionó a las mil maravillas para mí y simplificó enormemente mi desarrollo.