Ajouter des fichiers à un fichier zip avec Java

je suis en train d'extraire le contenu d'un fichier de guerre, puis d'ajouter de nouveaux fichiers à la structure du répertoire, puis de créer un nouveau fichier de guerre.

tout cela est fait programmatiquement à partir de Java - mais je me demande s'il ne serait pas plus efficace de copier le fichier war et puis juste ajouter les fichiers - alors je n'aurais pas à attendre aussi longtemps que la guerre se développe et doit ensuite être compressé à nouveau.

je n'arrive pas à trouver un moyen de le faire dans la documentation cependant ou n'importe quels exemples en ligne.

N'importe qui peut donner des conseils ou des conseils?

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 n'est pas possible de le faire).

Quelqu'un a-t-il de l'expérience ou des commentaires sur TrueZip ou peut-il recommander d'autres libaries similaires?

50
demandé sur Christian Schlichtherle 2010-02-08 20:15:41

12 réponses

en Java 7 Nous avons Zip File System qui permet d'ajouter et de changer des fichiers dans zip (jar, war) sans reconditionnement manuel.

nous pouvons écrire directement 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");
    }
}
72
répondu Grzegorz Żur 2013-07-06 15:05:04

Comme d'autres l'ont mentionné, il n'est pas possible d'ajouter du contenu à un zip (ou de guerre). Cependant, il est possible de créer un nouveau zip à la volée sans écrire temporairement le contenu extrait sur le disque. Il est difficile de deviner à quel point cela sera plus rapide, mais C'est le plus rapide que vous pouvez obtenir (du moins autant que je sache) avec Java standard. Comme L'a mentionné Carlos Tasada, SevenZipJBindings pourrait vous faire perdre quelques secondes supplémentaires, mais porter cette approche à SevenZipJBindings sera encore plus rapide. que d'utiliser des fichiers temporaires avec la même bibliothèque.

voici un code qui écrit le contenu d'un zip existant (war.zip) et ajoute un fichier supplémentaire (répondre.txt) à un nouveau zip (append.zip.) Tout ce qu'il faut, C'est Java 5 ou plus tard, pas besoin de bibliothèques supplémentaires.

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();
    }
}
42
répondu sfussenegger 2010-02-15 10:23:23

j'ai eu une demande similaire quelque part en arrière - mais c'était pour la lecture et l'écriture des archives zip (.le format de guerre devrait être similaire). J'ai essayé de le faire avec les flux Zip Java existants, mais j'ai trouvé la partie écriture encombrante - surtout quand les répertoires sont impliqués.

je vous recommande d'essayer la bibliothèque TrueZIP (open source - Apache STYLE licensed) qui expose n'importe quelle archive comme un système de fichier virtuel dans lequel vous pouvez lire et Ecrivez comme un système de fichiers normal. Cela a fonctionné comme un charme pour moi et a grandement simplifié mon développement.

24
répondu gnlogic 2017-06-19 08:20:34

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();
    }
}
13
répondu Liam Haworth 2012-01-12 02:04:11

Je ne connais pas de bibliothèque Java qui fasse 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 a raison: vous ne pouvez pas simplement "ajouter" des données à un fichier war ou zip, mais ce n'est pas parce qu'il y a une indication de "fin de fichier", à proprement parler, dans un fichier war. C'est parce que le format war (zip) inclut un répertoire, qui est normalement présent à la fin de la fichier contenant les métadonnées des différentes entrées du fichier war. Ajouter naïvement à un fichier war n'entraîne aucune mise à jour du répertoire, et donc vous avez juste un fichier war avec junk ajouté à lui.

ce qui est nécessaire est une classe intelligente qui comprend le format, et peut lire+mettre à jour un fichier war ou zip, y compris le répertoire selon le cas. DotNetZip le fait, sans décompresser/recompresser les entrées inchangées, comme vous l'avez décrit ou souhaité.

2
répondu Cheeso 2010-02-10 16:06:01

comme dit Cheeso, il n'y a aucun moyen de le faire. AFAIK les bouts avant de zip font exactement la même chose que vous à l'intérieur.

de toute façon si vous êtes inquiet de la vitesse d'extraction/compression de tout, vous pouvez essayer la bibliothèque SevenZipJBindings .

j'ai couvert cette bibliothèque dans mon blog il y a quelques mois (désolé pour l'auto-promotion). À titre d'exemple, extraire un fichier zip de 104 Mo en utilisant le java.util.zip m'a fallu 12 secondes, tandis que l'utilisation de cette bibliothèque a 4 secondes.

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

J'espère que ça aidera.

2
répondu Carlos Tasada 2010-02-12 11:41:10

voir ce rapport de bogue .

utilisant le mode ajout sur tout type de données structurées comme des fichiers zip ou tar les fichiers n'est pas quelque chose que vous pouvez vraiment attendez-vous à travailler. Ces formats de fichiers ont une valeur intrinsèque de "fin de fichier" indication intégrée dans le format de données.

si vous voulez vraiment sauter l'étape intermédiaire de un-waring / re-waring, vous pouvez lire le fichier war, obtenir tout le zip les entrées, puis écrivez à un nouveau dossier de guerre "ajoutant" les nouvelles entrées que vous vouliez ajouter. Pas parfait, mais au moins une solution automatisée.

1
répondu Michael Krauklis 2010-02-08 17:19:20

encore une autre Solution: vous pouvez trouver le code ci-dessous utile dans d'autres situations ainsi. J'ai utilisé ant de cette façon pour compiler des répertoires Java, générer des fichiers jar, mettre à 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);
    }
}
1
répondu Barmak 2011-03-28 19:33:51

c'est un code simple pour obtenir une réponse en utilisant 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);
            }
1
répondu erdem karayer 2014-09-11 13:12:20

Voici la version Java 1.7 de Liam answer qui utilise try with resources et Apache Commons IO.

La sortie est écrite dans un 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();
          }
        }
      }

    }

  }
0
répondu peceps 2014-01-28 09:04:36

cela fonctionne à 100% , si vous ne voulez pas utiliser des libs .. 1) tout d'abord, la classe qui ajoute des fichiers au 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());
0
répondu diego matos - keke 2016-02-16 20:00:56

Voici des exemples de la facilité avec laquelle les fichiers peuvent être ajoutés au zip existant en utilisant 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 fonctionnalités sous le capot lorsque cela est approprié, mais offre beaucoup plus de fonctionnalités comme la compression parallèle asynchrone sans fil.

attention aussi que le Zipfilesystem Java 7 par défaut est vulnérable à OutOfMemoryError sur d'énormes entrées.

0
répondu Vadzim 2017-05-23 12:17:36