Domanda

Attualmente sto estrarre il contenuto di un file war e poi l'aggiunta di alcuni nuovi file per la struttura di directory e quindi la creazione di un nuovo file war.

Tutto questo è stato fatto a livello di programmazione Java - ma mi chiedo se non sarebbe più efficiente copiare il file war e poi basta aggiungere il file", quindi non vorrei dover aspettare così a lungo come la guerra si espande e quindi deve essere compresso di nuovo.

Io non riesco a trovare un modo per fare questo nella documentazione o se qualche linea di esempi.

Chiunque può dare alcuni consigli o suggerimenti?

AGGIORNAMENTO:

TrueZip come accennato in una delle risposte sembra essere una buona libreria java per aggiungere a un file zip (nonostante altre risposte che dicono che non è possibile fare questo).

Qualcuno ha esperienza o feedback su TrueZip o può raccomandare altri simili biblioteche?

È stato utile?

Soluzione

In Java 7 abbiamo ottenuto Zip File System che permette l'aggiunta e la modifica di file in zIP (vaso, guerra) senza riconfezionamento manuale.

Possiamo scrivere direttamente al file all'interno di file zip, come nel seguente esempio.

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

Altri suggerimenti

Come altri hanno detto, non è possibile accodare il contenuto ad una zip esistente (o di guerra). Tuttavia, è possibile creare una nuova zip al volo senza scrivere temporaneamente contenuto estratto su disco. E 'difficile indovinare quanto più veloce questo sarà, ma è il più veloce è possibile ottenere (almeno per quanto ne so) con standard di Java. Come già detto da Carlos Tasada, SevenZipJBindings potrebbero spremere alcuni secondi in più, ma porting questo approccio alla SevenZipJBindings sarà ancora più veloce rispetto all'utilizzo di file temporanei con la stessa libreria.

Ecco un po 'di codice che scrive il contenuto di una zip (war.zip) esistenti e aggiunge un extra file (answer.txt) per una nuova zip (append.zip). Tutto ciò che serve è Java 5 o successivo, senza librerie extra necessario.

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

Ho avuto un requisito analogo qualche tempo indietro - ma era per la lettura e la scrittura di archivi zip (formato .war dovrebbe essere simile). Ho provato a fare con i flussi Java Zip esistenti, ma ho trovato ingombrante la parte di scrittura - specialmente quando le directory in cui i soggetti coinvolti.

Ti consiglio di provare il TrueZIP (aperta fonte - stile licenza Apache) libreria che espone qualsiasi archivio come un file system virtuale in cui è possibile leggere e scrivere come un filesystem normale. Ha funzionato come un fascino per me e notevolmente semplificato il mio sviluppo.

Si potrebbe utilizzare questo pezzo di codice che ho scritto

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

Non so di una libreria Java che fa ciò che si descrive. Ma quello che hai descritto è pratico. È possibile farlo in .NET, utilizzando DotNetZip .

Michael Krauklis è corretto che non si può semplicemente "aggiungere" i dati in un file di guerra o file zip, ma non lo è perché c'è un "fine del file" indicazione, a rigor di termini, in un file di guerra. È perché il formato di guerra (zip) include una directory, che è normalmente presente alla fine del file, che contiene i metadati per le varie voci nel file di guerra. Ingenuamente aggiungendo ad un file WAR risultati in nessun aggiornamento alla directory, e quindi basta avere un file guerra con spazzatura aggiunto ad esso.

ciò che è necessario è una classe intelligente che capisce il formato, e può leggere + aggiornare un file guerra o file zip, inclusa la directory a seconda dei casi. DotNetZip fa questo, senza decompressione / ricomprimere le voci invariate, proprio come hai descritto o desiderato.

Come dice Cheeso, non c'è modo di farlo. Per quanto ne so la zip front-end stanno facendo esattamente la stessa cosa, come si internamente.

In ogni caso, se siete preoccupati per la velocità di estrazione / comprimere tutto ciò, si consiglia di provare il SevenZipJBindings biblioteca.

ho ricoperto questa biblioteca nel mio blog qualche mese fa (scusate per l'auto-promozione). A titolo di esempio, l'estrazione di un file zip 104MB utilizzando il java.util.zip mi ha preso 12 secondi, durante l'utilizzo di questa libreria ha preso 4 secondi.

In entrambi i collegamenti si possono trovare esempi su come usarlo.

Speranza che aiuta.

bug report .

  

Utilizzando la modalità accodare su qualsiasi tipo di   dati strutturati come file zip o tar   file non è qualcosa che si può veramente   si aspettano di lavorare. Questi formati di file   avere una "fine del file" intrinseca   Indicazione costruito nel formato dei dati.

Se davvero si vuole saltare il passaggio intermedio di UN-Waring / re-Waring, è possibile leggere il file di guerra, ottenere tutte le voci di avviamento postale, quindi scrivere su un nuovo file di guerra "aggiungendo" le nuove voci che volevi aggiungere. Non perfetto, ma almeno una soluzione più automatizzata.

Ancora un altro Soluzione: Si possono trovare codice qui sotto utile in altre situazioni. Ho usato formica in questo modo per compilare le directory Java, generando file jar, l'aggiornamento dei file 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);
    }
}

questo è un semplice codice per ottenere una risposta con l'utilizzo di servlet e inviare una risposta

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

Ecco Java 1.7 versione di Liam risposta che utilizza provare con le risorse e Apache Commons IO.

L'output viene scritto in un nuovo file zip, ma può essere facilmente modificato per scrivere sul file originale.

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

    }

  }

questo funziona al 100%, se non volete usare librerie in più .. 1) In primo luogo, la classe che aggiungere file alla zip ..

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) si può chiamare nel vostro controller ..

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

Ecco alcuni esempi di come facilmente i file possono essere aggiunti al zip esistente utilizzando 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, il successore di TrueZIP, utilizza Java 7 NIO 2 caratteristiche sotto il cofano, se del caso, ma offre molto più caratteristiche come compressione parallela async thread-safe.

Attenzione anche che Java 7 ZipFileSystem di default è vulnerabili a OutOfMemoryError sugli ingressi enormi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top