2012-08-07 7 views
2

Quelqu'un peut-il me dire pourquoi le code suivant ne fonctionne pas? J'utilise l'API SharpZipLib pour les flux Zip, la dernière version DL'ed aujourd'hui de leur site. Je tente d'utiliser cette logique pour fusionner le contenu d'un fichier zip dans un autre, sans avoir à effectuer d'E/S sur le disque, car les fichiers zip prévus peuvent contenir des noms de fichiers réservés pour Windows. J'ai essayé ceci avec plusieurs différents fichiers zip de source et de destination (ceux qui contiennent des noms réservés et ceux qui ne le font pas). Le code ne lance aucune exception, et si vous inspectez le tampon avant chaque opération d'écriture, vous pouvez voir qu'il contient des données réelles, mais une fois l'opération terminée, la taille du fichier zip cible n'a pas changé et vous pouvez explorer pour confirmer qu'aucun nouveau fichier (ceux que le code est censé ajouter) n'a été ajouté au fichier de destination. :(Extraire les entrées zip dans un autre fichier Zip

public static void CopyToZip(string inArchive, string outArchive) 
    { 

     ZipOutputStream outStream = null; 
     ZipInputStream inStream = null; 
     try 
     { 
      outStream = new ZipOutputStream(File.OpenWrite(outArchive)); 
      outStream.IsStreamOwner = false; 
      inStream = new ZipInputStream(File.OpenRead(inArchive)); 
      ZipEntry currentEntry = inStream.GetNextEntry(); 
      while (currentEntry != null) 
      { 

       byte[] buffer = new byte[1024]; 
       ZipEntry newEntry = new ZipEntry(currentEntry.Name); 
       newEntry.Size = currentEntry.Size; 
       newEntry.DateTime = currentEntry.DateTime; 
       outStream.PutNextEntry(newEntry); 
       int size = 0; 
       while ((size = inStream.Read(buffer, 0, buffer.Length)) > 0) 
       { 
        outStream.Write(buffer, 0, size); 
       } 
       outStream.CloseEntry(); 

       currentEntry = inStream.GetNextEntry(); 
      } 
      outStream.IsStreamOwner = true; 
     } 
     catch (Exception e) 
     { 
      throw e; 
     } 
     finally 
     { 
      try { outStream.Close(); } 
      catch (Exception ignore) { } 
      try { inStream.Close(); } 
      catch (Exception ignore) { } 
     }  
    } 

Répondre

0

Une question que je vois est que vous ouvrez le fichier zip de sortie en utilisant File.OpenWrite(), qui remplacera le fichier de sortie existant au lieu de fusionner les nouvelles entrées en elle.

Il . est un exemple sur le Wiki SharpDevelop qui donne un exemple de mettre à jour un fichier zip en utilisant les flux de mémoire Il se trouve à http://wiki.sharpdevelop.net/SharpZipLib_Updating.ashx#Updating_a_zip_file_in_memory_1

+0

J'ai suivi ces exemples pour créer le code que j'ai posté ici. À ce stade, peu importe si elle écrase ou non, le code ci-dessus ne change pas du tout l'état du fichier zip de destination. S'il écrasait, au moins j'aurais quelque chose à déboguer ... Je n'ai pas besoin d'utiliser un flux de memeory. Ce que je veux dire par «sans utiliser le disque» est que je ne peux pas extraire le contenu d'un zip sur le disque dur, puis le remballer dans un autre zip. Il doit passer d'un zip à l'autre sans être déballé sur le disque dur en raison des conventions de nommage des fichiers Windows. –

+0

J'ai utilisé un nouveau FileStream (outArchive, FileMode.Open) à la place de File.OpwnWrite(). Il n'a pas changé le résultat du tout. –

1

J'ai fini par le faire en utilisant une API différente zip DotNet de http://dotnetzip.codeplex.com/ est la mise en œuvre ici..:

public static void CopyToZip(string inArchive, string outArchive, string tempPath) 
    { 
     ZipFile inZip = null; 
     ZipFile outZip = null; 

     try 
     { 
      inZip = new ZipFile(inArchive); 
      outZip = new ZipFile(outArchive); 
      List<string> tempNames = new List<string>(); 
      List<string> originalNames = new List<string>(); 
      int I = 0; 
      foreach (ZipEntry entry in inZip) 
      { 
       if (!entry.IsDirectory) 
       { 
        string tempName = Path.Combine(tempPath, "tmp.tmp"); 
        string oldName = entry.FileName; 
        byte[] buffer = new byte[4026]; 
        Stream inStream = null; 
        FileStream stream = null; 
        try 
        { 
         inStream = entry.OpenReader(); 
         stream = new FileStream(tempName, FileMode.Create, FileAccess.ReadWrite); 
         int size = 0; 
         while ((size = inStream.Read(buffer, 0, buffer.Length)) > 0) 
         { 
          stream.Write(buffer, 0, size); 
         } 
         inStream.Close(); 
         stream.Flush(); 
         stream.Close(); 
         inStream = new FileStream(tempName, FileMode.Open, FileAccess.Read); 

         outZip.AddEntry(oldName, inStream); 
         outZip.Save(); 
        } 
        catch (Exception exe) 
        { 
         throw exe; 
        } 
        finally 
        { 
         try { inStream.Close(); } 
         catch (Exception ignore) { } 
         try { stream.Close(); } 
         catch (Exception ignore) { } 
        } 
       } 
      } 

     } 
     catch (Exception e) 
     { 
      throw e; 
     } 
    } 
+0

Il contourne le problème de nom de fichier en utilisant un fichier temporaire (tmp.tmp) dans un répertoire donné, puis en donnant le nom original du fichier dans l'archive –

+0

Votre code fonctionnera bien, mais .. J'ai quelques notes pour vous . 1. votre code enregistre le fichier de sortie une fois pour chaque entrée qui est ajoutée. c'est inutile. 2. Vous pouvez éviter complètement l'écriture dans le système de fichiers. voir ma réponse pour plus de détails. – Cheeso

+0

Je devais appeler l'enregistrer là :(Lors de mes tests, quand j'ai mis en file d'attente la liste des fichiers à ajouter et essayé de les ajouter tous en même temps, avec un seul appel à la méthode Save() pour une raison quelconque le dossier est resté inchangé, je suis bien conscient de la performance giagantique que j'ai eu à épargner là où je le fais, mais malheureusement je ne pouvais pas le faire fonctionner de manière appropriée. –

0

Ce qui suit est un code plus simple qui va lire à partir du fichier zip d'entrée et écrire dans le fichier zip de sortie, qui existe potentiellement déjà. Il ne nécessite pas d'écrire des données temporaires sur le système de fichiers.

public static void CopyToZip(string inArchive, string outArchive) 
    { 
     using (inZip = new ZipFile(inArchive), 
      outZip = new ZipFile(outArchive)) 
     { 
      Func<String,Func<String,Stream>> getInStreamReturner = (name) => { 
       return new Func<String,Stream>(a){ return inZip[a].OpenReader(); }; 
      }; 
      foreach (ZipEntry entry in inZip) 
      { 
       if (!entry.IsDirectory) 
       { 
        string zipEntryName = entry.FileName; 
        outZip.AddEntry(zipEntryName, 
            getInStreamReturner(zipEntryName), 
            (name, stream) => stream.Close()); 
       } 
      } 
      outZip.Save(); 
     } 
    } 

Notes:

  1. Cette approche utilise la surcharge ZipFile.AddEntry qui accepte deux délégués: une ouverture et un plus. Ces fonctions sont appelées au moment de ZipFile.Save. L'ancien délégué doit ouvrir et renvoyer le flux contenant les données à compresser. Ce dernier doit simplement fermer le flux.

  2. Il est nécessaire de définir le getInStreamReturner Func, afin d'ouvrir le flux de droite au moment de ZipFile.Save. Gardez à l'esprit que le zipEntryName change de valeur à chaque fois dans la boucle. En outre ZipEntry.OpenReader() ouvre un flux sur les données zip réelles, qui lit et décompresse comme il va. Vous ne pouvez en avoir qu'un seul à la fois, par fichier ZipFile. getInStreamReturner crée une nouvelle fonction à chaque fois à travers la boucle, créant ainsi une fermeture pour conserver la valeur du zipEntryName pour référence au moment de ZipFile.Save.

  3. Cette approche échouera s'il existe des conflits de noms entre inArchive et outArchive. Pour éviter cela, vous devez vérifier cela et en quelque sorte l'éviter. Vous pouvez créer un nouveau nom unique ou ignorer l'ajout d'entrées avec des noms en double dans l'archive externe.

  4. Je n'ai pas testé cela.


Bien que cette approche n'écrit pas le système de fichiers, il -t décompresser et recompresser les données du fichier.Une requête ouverte permet à DotNetZip de migrer les entrées sans décompresser/recomprimer. Je ne l'ai pas encore implémenté.

Questions connexes