Frage

Ich bin derzeit Extrahieren des Inhalts einer WAR-Datei und dann einige neue Dateien in der Verzeichnisstruktur hinzufügen und dann einen neuen Krieg Datei.

Das ist alles programmatisch von Java getan - aber ich frage mich, ob es nicht effizienter wäre, die WAR-Datei zu kopieren und dann nur die Dateien anhängen - dann habe ich nicht so lange warten, wie der Krieg erweitert und dann wieder komprimiert werden muss.

Ich kann nicht scheinen, einen Weg zu finden, diese allerdings in der Dokumentation zu tun oder alle Online-Beispiele.

Jeder kann ein paar Tipps oder Hinweise geben?

UPDATE:

TrueZip wie in einer der Antworten erwähnt scheint eine sehr gute Java-Bibliothek, um zu einer ZIP-Datei (trotz anderer Antworten, die sagen, dass es nicht möglich ist, dies zu tun) anzuhängen.

Wer hat Erfahrung oder Feedback auf TrueZip haben oder andere ähnliche libaries empfehlen?

War es hilfreich?

Lösung

In Java 7 wir haben Zip File System dass erlaubt es, Dateien in zip (Glas, Krieg) ohne manuelle Umpacken hinzufügen und ändern.

Sie können direkt auf Dateien in ZIP-Dateien schreiben, wie im folgende Beispiel.

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");
    }
}

Andere Tipps

Wie andere schon erwähnt, ist es nicht möglich, Inhalte zu einem bestehenden Zip anhängen (oder Krieg). Allerdings ist es möglich, ein neues Zip-on the fly zu erstellen, ohne vorübergehend extrahierter Inhalt auf der Festplatte zu schreiben. Es ist schwer zu erraten, wie viel schneller das sein wird, aber es ist die schnellste Sie (zumindest soweit ich weiß) mit Standard-Java zu bekommen. Wie von Carlos Tasada erwähnt, SevenZipJBindings könnten Sie ein paar zusätzliche Sekunden Squeeze-out, aber dieser Ansatz SevenZipJBindings Portierung wird noch schneller sein als temporäre Dateien mit derselben Bibliothek.

Hier ist ein Code, der den Inhalt einer bestehenden zip (war.zip) und fügt eine zusätzliche Datei (answer.txt) in eine neue zip (append.zip) schreibt. Alles was man braucht ist Java 5 oder höher, keine zusätzlichen Bibliotheken erforderlich.

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();
    }
}

Ich hatte eine ähnliche Anforderung irgendwann zurück - aber es war zum Lesen und Schreiben von Zip-Archiven (.war-Format sollte ähnlich sein). Ich habe versucht, es mit dem vorhandenen Java Zip-Streams zu tun, aber das Schreiben Teil umständlich gefunden - vor allem, wenn Verzeichnissen, in denen beteiligt.

Ich werde Sie empfehlen die TrueZIP auszuprobieren (open Quelle - apache Stil lizenziert) Bibliothek, die jedes Archiv als ein virtuelles Dateisystem freigibt, in dem Sie wie ein normalen Dateisystem lesen und schreiben können. Es funktionierte wie ein Zauber für mich und meine Entwicklung stark vereinfacht.

Sie können dieses Stück Code verwende ich schrieb

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();
    }
}

Ich weiß nicht, von einer Java-Bibliothek, die das tut, was Sie beschreiben. Aber was Sie beschrieben ist praktisch. Sie können es in .NET tun, mit DotNetZip .

Michael Krauklis ist richtig, dass Sie nicht einfach „anhängen“ Daten in eine WAR-Datei oder Zip-Datei, aber es ist nicht, weil es ein „Ende der Datei“ -Anzeige ist, streng genommen, in einem Krieg-Datei. Es ist, weil der Krieg (zip) Format ein Verzeichnis enthält, das am Ende der Datei normalerweise vorhanden ist, die Metadaten für die verschiedenen Einträge in der WAR-Datei enthält. Naiv Anhängen an eine WAR-Datei führt kein Update auf das Verzeichnis, und so haben Sie nur eine WAR-Datei mit Junk angehängt.

Was notwendig ist, ist eine intelligente Klasse, die das Format versteht und + lesen kann einen Krieg oder ZIP-Datei aktualisieren, einschließlich des Verzeichnisses als angemessen. DotNetZip tut dies, ohne Dekomprimieren / recompressing die unveränderten Einträge, wie Sie es beschrieben oder gewünscht ist.

Wie Cheeso sagt, es gibt keinen Weg, es zu tun. AFAIK der Zip-Front-Ends machen genau das gleiche wie Sie intern.

Wie auch immer, wenn Sie über die Geschwindigkeit der Extraktion / Komprimieren alles besorgt sind, können Sie die SevenZipJBindings Bibliothek.

Ich bedeckte diese Bibliothek in meinem Blog vor einigen Monaten (sorry für die auto-Förderung). Nur als Beispiel, eine 104MB Zip-Datei mit dem java.util.zip Extraktion dauerte 12 Sekunden, während der Verwendung dieser Bibliothek dauerte 4 Sekunden.

In beiden Links können Sie Beispiele finden, wie es zu benutzen.

Hoffe, es hilft.

Sehen Sie diesen Bugreport rel="nofollow.

  

Mit Modus anhängen auf jede Art von   strukturierte Daten wie ZIP-Dateien oder tar   Dateien ist nicht etwas, das Sie wirklich   erwarten zu arbeiten. Diese Dateiformate   einen intrinsischen „Dateiende“   Anzeige eingebaut in das Datenformat.

Wenn Sie wirklich den Zwischenschritt der un-waring / re-waring überspringen möchten, können Sie die WAR-Datei Datei lesen, erhalten Sie alle Zip-Einträge, dann in einen neuen Krieg Datei schreiben „Anhängen“ die neuen Einträge Sie wollten hinzufügen. Nicht perfekt, aber zumindest eine automatisierte Lösung.

Und noch eine Lösung: Sie können auch Code unten nützlich in anderen Situationen finden. Ich habe ant verwendet diese Weise Java-Verzeichnisse zu erstellen, JAR-Dateien zu erzeugen, Zip-Dateien zu aktualisieren, ...

    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);
    }
}

dies ein einfacher Code erhält eine Antwort mit Servlet und eine Antwort senden

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);
            }

Hier ist Java 1.7 Version von Liam Antwort, die mit Ressourcen und Apache Commons IO verwendet versuchen.

Die Ausgabe wird auf eine neue Zip-Datei geschrieben, aber es kann leicht auf die Originaldatei schreiben verändert werden.

  /**
   * 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();
          }
        }
      }

    }

  }

das funktioniert 100%, wenn Sie nicht wollen zusätzliche Libs verwenden .. 1) zunächst die Klasse, die Dateien in den zip anhängen ..

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) Sie können es in Ihrem Controller nennen ..

//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());

Hier sind einige Beispiele, wie einfach Dateien an bestehende zip angehängt werden mit 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, der Nachfolger von TrueZIP verwendet Java 7 NIO 2 Funktionen unter der Haube bei Bedarf aber bietet viel mehr Funktionen wie thread-safe async parallele Kompression.

Beachten Sie auch, dass Java 7 ZipFileSystem ist standardmäßig

scroll top