Question

Je suis actuellement extrais le contenu d'un fichier de guerre, puis en ajoutant de nouveaux fichiers à la structure de répertoire et puis créer un nouveau fichier de guerre.

Tout cela est fait à partir de Java programatically - mais je me demande si ce ne serait pas plus efficace pour copier le fichier de guerre et puis juste ajouter les fichiers - alors je ne serais pas attendre si longtemps que la guerre se développe et puis doit être à nouveau comprimé.

Je ne peux pas sembler trouver une façon de le faire dans la documentation ou bien des exemples en ligne.

Tout le monde peut donner quelques conseils ou des pointeurs?

Mise à jour:

TrueZip comme mentionné dans l'une des réponses semble être une très bonne bibliothèque java pour ajouter à un fichier zip (malgré d'autres réponses qui disent qu'il est impossible de le faire).

Toute personne ont de l'expérience ou des commentaires sur TrueZip ou peut recommander d'autres similaires d'abord des bibliothèques?

Était-ce utile?

La solution

En Java 7 nous avons obtenu Système de fichiers Zip permet d'ajouter et de modifier les fichiers zip (pot, guerre) sans remballage manuel.

On peut écrire directement des fichiers dans les fichiers zip comme dans l'exemple suivant.

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

Autres conseils

Comme d'autres ont mentionné, il est impossible d'ajouter du contenu à un zip existant (ou la guerre). Cependant, il est possible de créer un nouveau zip à la volée sans écrire temporairement contenu extrait sur le disque. Il est difficile de deviner combien ce sera plus rapide, mais il est le plus rapide, vous pouvez obtenir (au moins autant que je sache) avec Java standard. Comme mentionné par Carlos Tasada, SevenZipJBindings peut presser quelques secondes sur vous supplémentaires, mais le portage de cette approche SevenZipJBindings sera encore plus rapide que d'utiliser les fichiers temporaires avec la même bibliothèque.

Voici un code qui écrit le contenu d'un zip existant (war.zip) et un fichier supplémentaire ajoute (de answer.txt) à un nouveau zip (append.zip). Il suffit Java 5 ou version ultérieure, aucune bibliothèque supplémentaire nécessaire.

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

J'ai eu une exigence similaire parfois en arrière - mais il était pour lire et écrire des archives zip (format .war doit être similaire). J'ai essayé de le faire avec les Java existants flux Zip, mais trouve la partie d'écriture lourde - surtout quand les répertoires dans lesquels impliqués.

Je vous recommande d'essayer le TrueZIP (ouverte source - le style apache bibliothèque sous licence) qui expose toutes les archives en tant que système de fichiers virtuel dans lequel vous pouvez lire et écrire comme un système de fichiers normal. Il a travaillé comme un charme pour moi et grandement simplifié mon développement.

Vous pouvez utiliser ce morceau de code que j'ai écrit

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

Je ne sais pas d'une bibliothèque Java qui fait ce que vous décrivez. Mais ce que vous avez décrit est pratique. Vous pouvez le faire dans .NET, en utilisant DotNetZip .

Michael Krauklis est exact que vous ne pouvez pas simplement les données « ajouter » à un fichier de guerre ou d'un fichier zip, mais il est pas parce qu'il ya une indication « fin de fichier », strictement parlant, dans un fichier de guerre. Il est parce que le format guerre (zip) comprend un répertoire, qui est normalement présente à la fin du fichier, qui contient des métadonnées pour les différentes entrées du fichier de guerre. annexant à Naïvement un fichier de résultats de la guerre en aucune mise à jour du répertoire, et si vous avez juste un fichier de guerre avec l'ordure qui y est annexé.

Ce qui est nécessaire est une classe intelligente qui comprend le format, et peut lire + mettre à jour un fichier de guerre ou d'un fichier zip, y compris le répertoire selon le cas. DotNetZip fait cela, sans décompression / recompression les entrées inchangées, tout comme vous avez décrit ou désiré.

Comme le dit Cheeso, il n'y a aucun moyen de le faire. AFAIK l'avant-extrémités zip font exactement la même chose que vous en interne.

Quoi qu'il en soit si vous êtes inquiet au sujet de la vitesse d'extraction / compression tout, vous pouvez essayer SevenZipJBindings .

Je couvert cette bibliothèque dans mon Blog il y a quelques mois (désolé pour la promotion de l'automobile). À titre d'exemple, l'extraction d'un fichier zip 104MB en utilisant le java.util.zip m'a pris 12 secondes, en utilisant cette bibliothèque a pris 4 secondes.

Dans les deux liens que vous pouvez trouver des exemples sur la façon de l'utiliser.

it helps.

Voir cette rapport .

  

En utilisant le mode ajouter sur tout type de   données structurées comme des fichiers zip ou tar   les fichiers ne sont pas quelque chose que vous pouvez vraiment   attendre à travailler. Ces formats de fichiers   ont une valeur intrinsèque « fin de fichier »   indication intégrée dans le format de données.

Si vous voulez vraiment sauter l'étape intermédiaire de non-Waring / re-Waring, vous pouvez lire le fichier de fichier de guerre, obtenir toutes les entrées zip, puis écrire dans un nouveau fichier de guerre « annexant » les nouvelles entrées que vous vouliez ajouter. Pas parfait, mais au moins une solution plus automatisée.

Encore une autre solution: Vous pouvez trouver le code ci-dessous utiles dans d'autres situations. Je l'ai utilisé ant cette façon de compiler les répertoires Java, la génération de fichiers jar, la mise à jour des fichiers 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);
    }
}

un code simple pour obtenir une réponse à l'utilisation de servlet et envoyer une réponse

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

Voici la version Java 1.7 de réponse Liam qui utilise les ressources et essayer avec Apache Commons IO.

La sortie est écrit dans un nouveau fichier zip mais il peut être facilement modifié pour écrire dans le fichier d'origine.

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

    }

  }

cela fonctionne 100%, si vous ne voulez pas utiliser libs supplémentaires .. 1) Tout d'abord, la classe qui append fichiers 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) vous pouvez l'appeler dans votre contrôleur ..

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

Voici des exemples comment facilement les fichiers peuvent être ajoutés à l'aide zip existant 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, le successeur de TrueZIP, utilise Java 7 NIO 2 fonctions sous le capot, le cas échéant, mais offre beaucoup plus de fonctionnalités comme thread-safe async compression parallèle.

Méfiez-vous également que Java 7 ZipFileSystem par défaut est

scroll top