Pregunta

Actualmente estoy extrayendo el contenido de un archivo war y luego agregando algunos archivos nuevos a la estructura del directorio y luego creando un nuevo archivo war.

Todo esto se hace programáticamente desde Java, pero me pregunto si no sería más eficiente copiar el archivo war y luego simplemente agregar los archivos; entonces no tendría que esperar tanto tiempo hasta que la guerra se expanda y luego tenga que volver a comprimirse.

Parece que no puedo encontrar una manera de hacer esto en la documentación ni en ningún ejemplo en línea.

¿Alguien puede dar algunos consejos o sugerencias?

ACTUALIZAR:

TrueZip, como se menciona en una de las respuestas, parece ser una muy buena biblioteca Java para agregar a un archivo zip (a pesar de otras respuestas que dicen que no es posible hacer esto).

¿Alguien tiene experiencia o comentarios sobre TrueZip o puede recomendar otras bibliotecas similares?

¿Fue útil?

Solución

En Java 7 conseguimos Sistema archivo zip que permite añadir y cambiar los archivos en zip (jAR), guerra sin reenvasado manual.

Podemos escribir directamente a los 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");
    }
}

Otros consejos

Como otros han dicho, no es posible añadir contenido a un zip existente (o la guerra). Sin embargo, es posible crear un nuevo zip sobre la marcha sin necesidad de escribir temporalmente contenido extraído el disco. Es difícil de adivinar cuánto más rápido este será, pero es el más rápido usted puede conseguir (al menos por lo que yo sé) con el estándar de Java. Como se ha mencionado por Carlos Tasada, SevenZipJBindings podrían exprimir que algunos segundos más, pero portar este enfoque para SevenZipJBindings todavía será más rápido que el uso de archivos temporales con la misma biblioteca.

Aquí hay un código que escribe el contenido de una postal (war.zip) existente y añade un archivo adicional (Answer.txt) a una nueva postal (append.zip). Sólo se necesita Java 5 o posterior, no hay bibliotecas adicionales necesarios.

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("42\n".getBytes());
        append.closeEntry();

        // close
        war.close();
        append.close();
    }
}

Yo tenía un requisito similar hace algún tiempo - pero era para leer y escribir archivos zip (formato .war debe ser similar). He intentado hacerlo con las corrientes de Java Zip existentes, pero comprobó que la pieza de escritura engorroso - sobre todo cuando directorios donde los involucrados.

Te recomiendo que para probar el TrueZIP (abierto fuente - estilo Apache License) biblioteca que expone cualquier archivo como un sistema de archivos virtual en el que se puede leer y escribir como un sistema de archivos normal. Funcionó como un encanto para mí y simplifica en gran medida el desarrollo de mi.

Se puede usar este fragmento de código que he escrito

public static void addFilesToZip(File source, File[] files)
{
    try
    {

        File tmpZip = File.createTempFile(source.getName(), null);
        tmpZip.delete();
        if(!source.renameTo(tmpZip))
        {
            throw new Exception("Could not make temp file (" + source.getName() + ")");
        }
        byte[] buffer = new byte[1024];
        ZipInputStream zin = new ZipInputStream(new FileInputStream(tmpZip));
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(source));

        for(int i = 0; i < files.length; i++)
        {
            InputStream in = new FileInputStream(files[i]);
            out.putNextEntry(new ZipEntry(files[i].getName()));
            for(int read = in.read(buffer); read > -1; read = in.read(buffer))
            {
                out.write(buffer, 0, read);
            }
            out.closeEntry();
            in.close();
        }

        for(ZipEntry ze = zin.getNextEntry(); ze != null; ze = zin.getNextEntry())
        {
            out.putNextEntry(ze);
            for(int read = zin.read(buffer); read > -1; read = zin.read(buffer))
            {
                out.write(buffer, 0, read);
            }
            out.closeEntry();
        }

        out.close();
        tmpZip.delete();
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
}

No sé de una biblioteca de Java que hace lo que describes. Pero lo que usted describe es práctico. Puede hacerlo en .NET, utilizando DotNetZip .

Michael Krauklis es cierto que no se puede simplemente "añadir" los datos a un archivo de guerra o archivo zip, pero no es porque hay un "final del archivo" indicación, en sentido estricto, en un archivo de la guerra. Es debido a que el formato de la guerra (zip) incluye un directorio, que normalmente está presente en el final del archivo, que contiene los metadatos de las diversas entradas en el archivo de la guerra. Ingenuamente anexar a un archivo WAR resultados en ninguna actualización en el directorio, y por lo que acaba de tener un archivo de la guerra de basura anexado a él.

Lo que se necesita es una clase inteligente que entiende el formato, y puede leer + actualizar un archivo de guerra o archivo zip, incluyendo el directorio, según corresponda. DotNetZip hace esto, sin descomprimir / volver a comprimir las entradas sin cambios, al igual que usted ha descrito o deseado.

Como dice Cheeso, no hay manera de hacerlo. Que yo sepa la cremallera frontales están haciendo exactamente lo mismo que usted internamente.

De todos modos, si usted está preocupado acerca de la velocidad de extracción / compresión de todo, es posible que desee probar el SevenZipJBindings biblioteca.

he cubierto esta biblioteca en mi Blog hace unos meses (lo siento por la auto-promoción). A modo de ejemplo, la extracción de un archivo zip 104MB utilizando el java.util.zip me llevó 12 segundos, mientras que el uso de esta biblioteca tomó 4 segundos.

En ambos enlaces se pueden encontrar ejemplos sobre cómo usarlo.

Espero que ayuda.

informe de error .

  

Uso del modo de añadir en cualquier tipo de   datos estructurados como archivos zip o tar   archivos no es algo que puede realmente   esperar a trabajar. Estos formatos de archivo   tener un "fin de fichero" intrínseca   indicación integrado en el formato de datos.

Si realmente desea omitir el paso intermedio de un-Waring / re-Waring, se podía leer el archivo de archivo de la guerra, conseguir todas las entradas zip, a continuación, escribir en un nuevo archivo de la guerra "anexar" las nuevas entradas que quería añadir. No es perfecto, pero al menos una solución más automatizada.

Sin embargo, otro Solución: Es posible encontrar el código de abajo útil en otras situaciones también. He utilizado esta forma de hormiga para compilar directorios Java, la generación de archivos jar, la actualización de los archivos zip, ...

    public static void antUpdateZip(String zipFilePath, String libsToAddDir) {
    Project p = new Project();
    p.init();

    Target target = new Target();
    target.setName("zip");
    Zip task = new Zip();
    task.init();
    task.setDestFile(new File(zipFilePath));
    ZipFileSet zipFileSet = new ZipFileSet();
    zipFileSet.setPrefix("WEB-INF/lib");
    zipFileSet.setDir(new File(libsToAddDir));
    task.addFileset(zipFileSet);
    task.setUpdate(true);

    task.setProject(p);
    task.init();
    target.addTask(task);
    target.setProject(p);
    p.addTarget(target);

    DefaultLogger consoleLogger = new DefaultLogger();
    consoleLogger.setErrorPrintStream(System.err);
    consoleLogger.setOutputPrintStream(System.out);
    consoleLogger.setMessageOutputLevel(Project.MSG_DEBUG);
    p.addBuildListener(consoleLogger);

    try {
        // p.fireBuildStarted();

        // ProjectHelper helper = ProjectHelper.getProjectHelper();
        // p.addReference("ant.projectHelper", helper);
        // helper.parse(p, buildFile);
        p.executeTarget(target.getName());
        // p.fireBuildFinished(null);
    } catch (BuildException e) {
        p.fireBuildFinished(e);
        throw new AssertionError(e);
    }
}

este es un código simple para obtener una respuesta usando servlet y enviar una respuesta

myZipPath = bla bla...
    byte[] buf = new byte[8192];
    String zipName = "myZip.zip";
    String zipPath = myzippath+ File.separator+"pdf" + File.separator+ zipName;
    File pdfFile = new File("myPdf.pdf");
    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipPath));
    ZipEntry zipEntry = new ZipEntry(pdfFile.getName());
    out.putNextEntry(zipEntry);
    InputStream in = new FileInputStream(pdfFile);
    int len;
    while ((len = in.read(buf)) > 0) {
         out.write(buf, 0, len);
     }
    out.closeEntry();
    in.close();
     out.close();
                FileInputStream fis = new FileInputStream(zipPath);
                response.setContentType("application/zip");
                response.addHeader("content-disposition", "attachment;filename=" + zipName);
    OutputStream os = response.getOutputStream();
            int length = is.read(buffer);
            while (length != -1)
            {
                os.write(buffer, 0, length);
                length = is.read(buffer);
            }

Esta es la versión Java de respuesta Liam 1.7 que utiliza tratar con los recursos y Apache Commons IO.

La salida se escribe en un nuevo archivo zip pero puede ser fácilmente modificado para escribir en el archivo original.

  /**
   * Modifies, adds or deletes file(s) from a existing zip file.
   *
   * @param zipFile the original zip file
   * @param newZipFile the destination zip file
   * @param filesToAddOrOverwrite the names of the files to add or modify from the original file
   * @param filesToAddOrOverwriteInputStreams the input streams containing the content of the files
   * to add or modify from the original file
   * @param filesToDelete the names of the files to delete from the original file
   * @throws IOException if the new file could not be written
   */
  public static void modifyZipFile(File zipFile,
      File newZipFile,
      String[] filesToAddOrOverwrite,
      InputStream[] filesToAddOrOverwriteInputStreams,
      String[] filesToDelete) throws IOException {


    try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(newZipFile))) {

      // add existing ZIP entry to output stream
      try (ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFile))) {
        ZipEntry entry = null;
        while ((entry = zin.getNextEntry()) != null) {
          String name = entry.getName();

          // check if the file should be deleted
          if (filesToDelete != null) {
            boolean ignoreFile = false;
            for (String fileToDelete : filesToDelete) {
              if (name.equalsIgnoreCase(fileToDelete)) {
                ignoreFile = true;
                break;
              }
            }
            if (ignoreFile) {
              continue;
            }
          }

          // check if the file should be kept as it is
          boolean keepFileUnchanged = true;
          if (filesToAddOrOverwrite != null) {
            for (String fileToAddOrOverwrite : filesToAddOrOverwrite) {
              if (name.equalsIgnoreCase(fileToAddOrOverwrite)) {
                keepFileUnchanged = false;
              }
            }
          }

          if (keepFileUnchanged) {
            // copy the file as it is
            out.putNextEntry(new ZipEntry(name));
            IOUtils.copy(zin, out);
          }
        }
      }

      // add the modified or added files to the zip file
      if (filesToAddOrOverwrite != null) {
        for (int i = 0; i < filesToAddOrOverwrite.length; i++) {
          String fileToAddOrOverwrite = filesToAddOrOverwrite[i];
          try (InputStream in = filesToAddOrOverwriteInputStreams[i]) {
            out.putNextEntry(new ZipEntry(fileToAddOrOverwrite));
            IOUtils.copy(in, out);
            out.closeEntry();
          }
        }
      }

    }

  }

Esto funciona al 100%, si usted no desea utilizar librerías adicionales .. 1) En primer lugar, la clase que anexar archivos a la cremallera ..

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class AddZip {

    public void AddZip() {
    }

    public void addToZipFile(ZipOutputStream zos, String nombreFileAnadir, String nombreDentroZip) {
        FileInputStream fis = null;
        try {
            if (!new File(nombreFileAnadir).exists()) {//NO EXISTE 
                System.out.println(" No existe el archivo :  " + nombreFileAnadir);return;
            }
            File file = new File(nombreFileAnadir);
            System.out.println(" Generando el archivo '" + nombreFileAnadir + "' al ZIP ");
            fis = new FileInputStream(file);
            ZipEntry zipEntry = new ZipEntry(nombreDentroZip);
            zos.putNextEntry(zipEntry);
            byte[] bytes = new byte[1024];
            int length;
            while ((length = fis.read(bytes)) >= 0) {zos.write(bytes, 0, length);}
            zos.closeEntry();
            fis.close();

        } catch (FileNotFoundException ex ) {
            Logger.getLogger(AddZip.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(AddZip.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }

}

2) se le puede llamar en su controlador ..

//in the top
try {
fos = new FileOutputStream(rutaZip);
zos =   new ZipOutputStream(fos);
} catch (FileNotFoundException ex) {
Logger.getLogger(UtilZip.class.getName()).log(Level.SEVERE, null, ex);
}

...
//inside your method
addZip.addToZipFile(zos, pathFolderFileSystemHD() + itemFoto.getNombre(), "foto/" + itemFoto.getNombre());

Estos son algunos ejemplos de cómo fácilmente archivos se pueden adjuntar a zip existente utilizando TrueVFS :

// append a file to archive under different name
TFile.cp(new File("existingFile.txt"), new TFile("archive.zip", "entry.txt"));

// recusively append a dir to the root of archive
TFile src = new TFile("dirPath", "dirName");
src.cp_r(new TFile("archive.zip", src.getName()));

TrueVFS, el sucesor de TrueZIP, utiliza Java 7 NiO2 características bajo el capó cuando sea apropiado, pero ofrece mucho más características como compresión paralela async thread-safe.

Cuidado también que Java 7 ZipFileSystem por defecto es vulnerables a OutOfMemoryError en grandes entradas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top