2012-08-07 113 views
2

誰能告訴我爲什麼下面的代碼不起作用?我正在使用SharpZipLib API用於Zip流,這是目前從他們的網站上DL'ed的最新版本。我試圖使用這個邏輯來合併一個zip文件的內容到另一個,而不必在磁盤上執行IO,因爲預期的zip文件可能包含保留的windows文件名。我已經嘗試過使用多個不同的源和目標zip文件(包含保留名稱和不包含這些名稱的文件)。代碼不會拋出任何異常,並且如果在每次寫入操作之前檢查緩衝區,則可以看到它包含真實數據,但在整個操作完成後,目標zip文件的大小沒有改變,您可以探索它確認沒有新文件(代碼應該添加的文件)實際上已添加到目標文件。 :(將zip條目提取到另一個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) { } 
     }  
    } 

回答

0

一個問題,我看到的是,您打開使用File.OpenWrite()輸出zip文件,這將替換現有的輸出文件,而不是合併新條目進去。

有是SharpDevelop Wiki上的一個例子,它提供了一個使用內存流更新zip文件的例子,它可以在http://wiki.sharpdevelop.net/SharpZipLib_Updating.ashx#Updating_a_zip_file_in_memory_1

+0

我遵循這些示例來創建我在此處發佈的代碼。此時不管它是否覆蓋,上面的代碼根本不會改變目標zip文件的狀態。如果它是覆蓋,至少我會有東西來調試......我不需要使用memeory流。我的意思是「不使用磁盤」是因爲我無法將一個zip文件的內容提取到硬盤上,然後將其重新打包到另一個zip文件中。由於windows文件的命名規則,它必須從一個zip文件移動到下一個文件,而不需要在HD上解壓縮。 – 2012-08-07 21:38:59

+0

我使用了File.OpwnWrite()的新FileStream(outArchive,FileMode.Open)。它根本沒有改變結果。 – 2012-08-07 21:45:43

1

我使用不同的API來完成這個操作,DotNet zip從http://dotnetzip.codeplex.com/下面是實現:

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

它通過在給定目錄中使用臨時文件(tmp.tmp)來解決文件名問題,然後在文件夾中給出文件的原始名稱 – 2012-08-08 05:07:08

+0

您的代碼可以正常工作,但是..我爲您準備了一些註釋。 1.您的代碼爲每個添加的條目保存一次輸出文件。這是不必要的。 2.你可以避免完全寫入文件系統。詳情請參閱我的答案。 – Cheeso 2012-10-30 17:58:45

+0

我不得不打電話給那裏:(在我的測試中,當我排隊列出要添加的文件並試圖一次添加它們時,無論出於何種原因,只需一次調用Save()方法,該文件保持不變,我很清楚我在那裏所做的巨大的性能打擊,但不幸的是我無法以合適的方式工作。 – 2012-11-05 19:15:35

0

以下是一些簡單的代碼,它將從輸入zip讀取並寫入輸出zip,它可能已經存在。它不需要向文件系統寫入臨時數據。

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

注:

  1. 這種方法使用的過載ZipFile.AddEntry接受兩個代表:一個開罐器和一個更接近。這些功能在ZipFile.Save時稱爲。前一個委託需要打開並返回包含要壓縮數據的流。後者代表需要關閉流。

  2. 有必要定義getInStreamReturner Func,以便在ZipFile.Save時打開正確的流。請記住,zipEntryName每次在循環中都會更改值。此外,ZipEntry.OpenReader()會在實際的zip數據上打開一個數據流,該數據會隨着讀取和解壓縮進行。您可以在任何時間,每個ZipFile中只打開其中一個。 getInStreamReturner每次通過循環創建一個新函數,從而創建一個閉包以保留zipEntryName的值以供在ZipFile.Save時參考。

  3. 如果inArchive和outArchive之間存在名稱衝突,則此方法將失敗。爲了避免這一點,你需要檢查並避免它。要麼設計一個新的唯一名稱,要麼跳過將具有重複名稱的條目添加到outarchive中。

  4. 我還沒有測試過這個。


雖然這種方法不能寫入文件系統,它確實解壓縮和重新壓縮文件中的數據。有一個開放的請求,爲DotNetZip提供一個功能,用於在沒有解壓縮/重新壓縮跳轉的情況下遷移條目。我還沒有實現。