42 votes

java.util.zip - Recréation de la structure de répertoire

Tout en essayant de compresser une archive en utilisant l' java.util.zip j'ai rencontré beaucoup de problèmes dont la plupart je l'ai résolu. Maintenant que je reçois enfin une partie de la sortie j'ai du mal à obtenir que le "droit de sortie". J'ai un extrait de fichier ODT (répertoire serait plus raccord d'une description) à qui j'ai fait quelques modifications. Maintenant, je veux compresser le répertoire en tant que de recréer le fichier ODT structure. La compression de l'annuaire et de le renommer à la fin .odt fonctionne bien, donc il devrait y avoir aucun problème.

Le principal problème est que je perds la structure interne de l'annuaire. Tout devient "à plat" et je n'arrive pas à trouver un moyen de conserver l'original de la structure multi-couches. J'apprécierais un peu d'aide sur ce que je n'arrive pas a trouver le problème.

Voici les extraits de code:

ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
    FILEPATH.substring(0, FILEPATH.lastIndexOf(SEPARATOR) + 1).concat("test.zip")));
    compressDirectory(TEMPARCH, out);

L' SEPARATOR est le système de séparateur de fichiers et l' FILEPATH est le chemin de l'original ODT que je vais remplacer mais ne l'ont pas fait ici à des fins de test. J'écris simplement pour un test.zip fichier dans le même répertoire.

private void compressDirectory(String directory, ZipOutputStream out) throws IOException
{
    File fileToCompress = new File(directory);
    // list contents.
    String[] contents = fileToCompress.list();
    // iterate through directory and compress files.
    for(int i = 0; i < contents.length; i++)
    {
        File f = new File(directory, contents[i]);
        // testing type. directories and files have to be treated separately.
        if(f.isDirectory())
        {
            // add empty directory
            out.putNextEntry(new ZipEntry(f.getName() + SEPARATOR));
            // initiate recursive call
            compressDirectory(f.getPath(), out);
            // continue the iteration
            continue;
        }else{
             // prepare stream to read file.
             FileInputStream in = new FileInputStream(f);
             // create ZipEntry and add to outputting stream.
             out.putNextEntry(new ZipEntry(f.getName()));
             // write the data.
             int len;
             while((len = in.read(data)) > 0)
             {
                 out.write(data, 0, len);
             }
             out.flush();
             out.closeEntry();
             in.close();
         }
     }
 }

Le répertoire qui contient les fichiers de zip est quelque part dans l'espace utilisateur et pas dans le même répertoire que le fichier résultant. Je suppose que cela pourrait être un problème, mais je ne peux pas vraiment voir comment. Aussi j'ai pensé que le problème pouvait être en utilisant le même flux de sortie mais encore une fois je ne vois pas comment. J'ai vu dans certains des exemples et des tutoriels qu'ils utilisent getPath() au lieu de getName() mais le changement qui me donne un vide de fichier zip.

96voto

McDowell Points 62645

L' URI de la classe est utile pour travailler avec des chemins relatifs.

File mydir = new File("C:\\mydir");
File myfile = new File("C:\\mydir\\path\\myfile.txt");
System.out.println(mydir.toURI().relativize(myfile.toURI()).getPath());

Le code ci-dessus va émettre la chaîne path/myfile.txt.

Pour être complet, voici un zip méthode pour l'archivage d'un répertoire:

  public static void zip(File directory, File zipfile) throws IOException {
    URI base = directory.toURI();
    Deque<File> queue = new LinkedList<File>();
    queue.push(directory);
    OutputStream out = new FileOutputStream(zipfile);
    Closeable res = out;
    try {
      ZipOutputStream zout = new ZipOutputStream(out);
      res = zout;
      while (!queue.isEmpty()) {
        directory = queue.pop();
        for (File kid : directory.listFiles()) {
          String name = base.relativize(kid.toURI()).getPath();
          if (kid.isDirectory()) {
            queue.push(kid);
            name = name.endsWith("/") ? name : name + "/";
            zout.putNextEntry(new ZipEntry(name));
          } else {
            zout.putNextEntry(new ZipEntry(name));
            copy(kid, zout);
            zout.closeEntry();
          }
        }
      }
    } finally {
      res.close();
    }
  }

Ce code permet de ne pas conserver les dates et je ne suis pas sûr de savoir comment il allait réagir à des choses comme des liens symboliques. Aucune tentative n'est faite pour ajouter des entrées de répertoire, de sorte que les répertoires vides ne seront pas inclus.

La correspondante unzip commande:

  public static void unzip(File zipfile, File directory) throws IOException {
    ZipFile zfile = new ZipFile(zipfile);
    Enumeration<? extends ZipEntry> entries = zfile.entries();
    while (entries.hasMoreElements()) {
      ZipEntry entry = entries.nextElement();
      File file = new File(directory, entry.getName());
      if (entry.isDirectory()) {
        file.mkdirs();
      } else {
        file.getParentFile().mkdirs();
        InputStream in = zfile.getInputStream(entry);
        try {
          copy(in, file);
        } finally {
          in.close();
        }
      }
    }
  }

Utilitaire les méthodes sur lesquelles ils s'appuient:

  private static void copy(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    while (true) {
      int readCount = in.read(buffer);
      if (readCount < 0) {
        break;
      }
      out.write(buffer, 0, readCount);
    }
  }

  private static void copy(File file, OutputStream out) throws IOException {
    InputStream in = new FileInputStream(file);
    try {
      copy(in, out);
    } finally {
      in.close();
    }
  }

  private static void copy(InputStream in, File file) throws IOException {
    OutputStream out = new FileOutputStream(file);
    try {
      copy(in, out);
    } finally {
      out.close();
    }
  }

La taille de la mémoire tampon est entièrement arbitraire.

7voto

ZZ Coder Points 36990

Je vois 2 problèmes dans votre code,

  1. Vous n'enregistrez pas le chemin du répertoire, il n'y a donc aucun moyen de le récupérer.
  2. Sous Windows, vous devez utiliser "/" comme séparateur de chemin. Certains programmes de décompression n'aiment pas \.

J'inclus ma propre version pour votre référence. Nous utilisons celui-ci pour compresser des photos et les télécharger afin que cela fonctionne avec divers programmes de décompression. Il préserve la structure des répertoires et les horodatages.

   public static void createZipFile(File srcDir, OutputStream out,
   boolean verbose) throws IOException {

  List<String> fileList = listDirectory(srcDir);
  ZipOutputStream zout = new ZipOutputStream(out);

  zout.setLevel(9);
  zout.setComment("Zipper v1.2");

  for (String fileName : fileList) {
   File file = new File(srcDir.getParent(), fileName);
   if (verbose)
    System.out.println("  adding: " + fileName);

   // Zip always use / as separator
   String zipName = fileName;
   if (File.separatorChar != '/')
    zipName = fileName.replace(File.separatorChar, '/');
   ZipEntry ze;
   if (file.isFile()) {
    ze = new ZipEntry(zipName);
    ze.setTime(file.lastModified());
    zout.putNextEntry(ze);
    FileInputStream fin = new FileInputStream(file);
    byte[] buffer = new byte[4096];
    for (int n; (n = fin.read(buffer)) > 0;)
     zout.write(buffer, 0, n);
    fin.close();
   } else {
    ze = new ZipEntry(zipName + '/');
    ze.setTime(file.lastModified());
    zout.putNextEntry(ze);
   }
  }
  zout.close();
 }

 public static List<String> listDirectory(File directory)
   throws IOException {

  Stack<String> stack = new Stack<String>();
  List<String> list = new ArrayList<String>();

  // If it's a file, just return itself
  if (directory.isFile()) {
   if (directory.canRead())
    list.add(directory.getName());
   return list;
  }

  // Traverse the directory in width-first manner, no-recursively
  String root = directory.getParent();
  stack.push(directory.getName());
  while (!stack.empty()) {
   String current = (String) stack.pop();
   File curDir = new File(root, current);
   String[] fileList = curDir.list();
   if (fileList != null) {
    for (String entry : fileList) {
     File f = new File(curDir, entry);
     if (f.isFile()) {
      if (f.canRead()) {
       list.add(current + File.separator + entry);
      } else {
       System.err.println("File " + f.getPath()
         + " is unreadable");
       throw new IOException("Can't read file: "
         + f.getPath());
      }
     } else if (f.isDirectory()) {
      list.add(current + File.separator + entry);
      stack.push(current + File.separator + f.getName());
     } else {
      throw new IOException("Unknown entry: " + f.getPath());
     }
    }
   }
  }
  return list;
 }
}
 

4voto

user769087 Points 91

Il suffit d'aller dans la source de java.util.zip.ZipEntry. Il traite ZipEntry comme répertoire si son nom se termine par "/" personnages. Juste suffixe le nom du répertoire par "/". Aussi, vous devez retirer le disque préfixe à faire par rapport.

Cochez cette exemple pour la compression de seulement les répertoires vides,

http://bethecoder.com/applications/tutorials/showTutorials.action?tutorialId=Java_ZipUtilities_ZipEmptyDirectory

Tant que vous êtes en mesure de créer à la fois vide et non-répertoires vides dans le fichier ZIP, votre structure de répertoire est intact.

Bonne chance.

3voto

fcarriedo Points 96

Voici un autre exemple (récursif) qui vous permet également d'inclure / exclure le dossier contenant le zip:

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipUtil {

  private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

  public static void main(String[] args) throws Exception {
    zipFile("C:/tmp/demo", "C:/tmp/demo.zip", true);
  }

  public static void zipFile(String fileToZip, String zipFile, boolean excludeContainingFolder)
    throws IOException {        
    ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));    

    File srcFile = new File(fileToZip);
    if(excludeContainingFolder && srcFile.isDirectory()) {
      for(String fileName : srcFile.list()) {
        addToZip("", fileToZip + "/" + fileName, zipOut);
      }
    } else {
      addToZip("", fileToZip, zipOut);
    }

    zipOut.flush();
    zipOut.close();

    System.out.println("Successfully created " + zipFile);
  }

  private static void addToZip(String path, String srcFile, ZipOutputStream zipOut)
    throws IOException {        
    File file = new File(srcFile);
    String filePath = "".equals(path) ? file.getName() : path + "/" + file.getName();
    if (file.isDirectory()) {
      for (String fileName : file.list()) {             
        addToZip(filePath, srcFile + "/" + fileName, zipOut);
      }
    } else {
      zipOut.putNextEntry(new ZipEntry(filePath));
      FileInputStream in = new FileInputStream(srcFile);

      byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
      int len;
      while ((len = in.read(buffer)) != -1) {
        zipOut.write(buffer, 0, len);
      }

      in.close();
    }
  }
}
 

2voto

Alexi Points 57

Si vous ne voulez pas vous soucier des flux d'entrée d'octets, de la taille des tampons et d'autres détails de bas niveau. Vous pouvez utiliser les bibliothèques Zip d'Ant à partir de votre code java (les dépendances maven se trouvent ici ). Voici maintenant je fais un zip comprenant une liste de fichiers et répertoires:

 public static void createZip(File zipFile, List<String> fileList) {

    Project project = new Project();
    project.init();

    Zip zip = new Zip();
    zip.setDestFile(zipFile);
    zip.setProject(project);

    for(String relativePath : fileList) {

        //noramalize the path (using commons-io, might want to null-check)
        String normalizedPath = FilenameUtils.normalize(relativePath);

        //create the file that will be used
        File fileToZip = new File(normalizedPath);
        if(fileToZip.isDirectory()) {
            ZipFileSet fileSet = new ZipFileSet();
            fileSet.setDir(fileToZip);
            fileSet.setPrefix(fileToZip.getPath());
            zip.addFileset(fileSet);
        } else {
            FileSet fileSet = new FileSet();
            fileSet.setDir(new File("."));
            fileSet.setIncludes(normalizedPath);
            zip.addFileset(fileSet);
        }
    }

    Target target = new Target();
    target.setName("ziptarget");
    target.addTask(zip);
    project.addTarget(target);
    project.executeTarget("ziptarget");
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X