簡體   English   中英

DotNetZip從其他zip的子集創建zip

[英]DotNetZip creating zip from subset of other zip

我有一個大的zip文件,我需要分成多個zip文件。 在我正在創建的方法中,我有一個List對象。

這是我得到的代碼:

 //All files have the same basefilename/
 string basefilename = Path.GetFileNameWithoutExtension(entries[0].FileName);
 MemoryStream memstream = new MemoryStream();
 ZipFile zip = new ZipFile();
 foreach (var entry in entries)
 {
    string newFileName = basefilename + Path.GetExtension(entry.FileName);
    zip.AddEntry(newFileName, entry.OpenReader());
 }

 zip.Save(memstream);

 //this will later go in an file-io handler class.
 FileStream outstream = File.OpenWrite(@"c:\files\"+basefilename+ ".zip");
 memstream.WriteTo(outstream);
 outstream.Flush();
 outstream.Close();

這是我在save()調用時遇到的錯誤:

{Ionic.Zlib.ZlibException:在Ionic.Zlib.ZlibBodec.Inflate(FlushType flush)的Ionic.Zlib.InflateManager.Inflate(FlushType flush)中的錯誤狀態(無效塊類型),位於Ionic.Zlib.ZlibBaseStream.Read(Byte [] Ionic.Zlib.DeflateStream.Read(Byte []緩沖區,Int32偏移量,Int32計數)的Ionic.Crc.CrcCalculatorStream.Read(Byte []緩沖區,Int32偏移量,Int32計數)的離子,Ionic,Int32偏移量,Int32計數) .Zip.SharedUtilities.ReadWithRetry(Stream s,Byte [] buffer,Int32 offset,Int32 count,String FileName)在離子的Ionic.Zip.ZipEntry.Write(Stream s)的Ionic.Zip.ZipEntry._WriteEntryData(Stream s) .Zip.ZipFile.Save()在Ionic.Zip.ZipFile.Save(Stream outputStream)at at

我究竟做錯了什么?

這就是你做錯了:你在一個ZipFile實例中有多個對ZipEntry.OpenReader()的掛起調用。 最多只能有一個掛起的ZipEntry.OpenReader()。

原因如下:當您使用ZipFile.Read()或新的ZipFile()實例化給定的zip文件時,只創建了一個Stream對象,並傳遞現有文件的名稱。 當您調用ZipEntry.OpenReader()時,它會在Stream對象中生成Seek(),以將文件指針移動到該特定條目的壓縮字節流的開頭。 當您再次調用ZipEntry.OpenReader()時,它會導致另一個Seek()到流中的其他位置。 因此,通過添加條目並連續調用OpenReader(),您將重復調用Seek(),但只有最后一個有效。 流游標將放置在對應於最后一次調用ZipEntry.OpenReader()的條目的數據的開頭。

解決它:廢棄你的方法。 使用比現有zip文件更少的條目創建新zipfile的最簡單方法是:通過讀取現有文件來實例化ZipFile,然后刪除不需要的條目,然后將ZipFile.Save()調用到新路徑。

using (var zip = ZipFile.Read("c:\\dir\\path\\to\\existing\\zipfile.zip")) 
{
    foreach (var name in namesToRemove) // IEnumerable<String>
    {
       zip[name].Remove();
    }
    zip.Save("c:\\path\\to\\new\\Archive.zip");
} 

編輯
在調用Save()時這會做什么:庫讀取未從文件系統文件中刪除的條目的原始壓縮數據,並將它們寫入新的存檔文件。 這非常快,因為它不會對每個條目進行解壓縮和重新壓縮,以便將其放入新的較小的zip文件中。 基本上它從原始zip文件中讀取二進制數據片段,並將它們連接在一起以形成新的較小的zip文件。

要生成多個較小的文件,可以使用原始zip文件重復執行此操作; 只需將上面的內容包裝在循環中,然后更改刪除的文件以及新的較小存檔的文件名。 讀取現有的zip文件也非常快。


作為替代方案,您可以解壓縮並提取每個條目,然后重新壓縮並將條目寫入新的zip文件。 這是漫長的過程,但它是可能的。 在這種情況下,對於要創建的每個較小的zip文件,您需要創建兩個ZipFile實例。 通過閱讀原始zip存檔打開第一個。 對於要保留的每個條目,創建一個MemoryStream,從條目中提取內容到該MemoryStream中,並記住在mem流中調用Seek()以重置內存流上的光標。 然后使用第二個ZipFile實例,調用AddEntry(),使用該MemoryStream作為添加條目的源。 僅在第二個實例上調用ZipFile.Save()。

using (var orig = ZipFile.Read("C:\\whatever\\OriginalArchive.zip"))
{
    using (var smaller = new ZipFile())
    {
      foreach (var name in entriesToKeep) 
      { 
         var ms = new MemoryStream();
         orig[name].Extract(ms); // extract into stream
         ms.Seek(0,SeekOrigin.Begin);
         smaller.AddEntry(name,ms);
      }
      smaller.Save("C:\\location\\of\\SmallerZip.zip");
    }   
}

這有效,但它涉及到每個條目的解壓縮和再壓縮,這些條目進入較小的zip,這是低效且不必要的。


如果你不介意解壓縮和重新壓縮的低效率,你可以使用另一種方法:調用ZipFile.AddEntry()重載,接受開啟者和更接近的委托 這樣做是將對OpenReader()的調用推遲到將條目寫入新的較小的zip文件的時間。 結果是你一次只有一個掛起的OpenReader()。

using(ZipFile original = ZipFile.Read("C:\\path.to\\original\\Archive.zip"),
      smaller = new ZipFile())
{
    foreach (var name in entriesToKeep)
    {
        zip.AddEntry(zipEntryName,
                     (name) => original[name].OpenReader(),
                     null);
    }

    smaller.Save("C:\\path.to\\smaller\\Archive.zip");
}

它仍然效率低下,因為每個條目都經過解壓縮和重新壓縮,但效率低一點。

Cheeso指出我不能打開多個讀者。 雖然他的移除解決方案不是我需要的。 所以我嘗試用新知識解決問題,這就是我創造的。

string basefilename = Path.GetFileNameWithoutExtension(entries[0].FileName);
ZipFile zip = new ZipFile();
foreach (var entry in entries){
      CrcCalculatorStream reader = entry.OpenReader();
      MemoryStream memstream = new MemoryStream();
      reader.CopyTo(memstream);
      byte[] bytes = memstream.ToArray();
      string newFileName = basefilename + Path.GetExtension(entry.FileName);
      zip.AddEntry(newFileName, bytes);
}

zip.Save(@"c:\files\" + basefilename + ".zip");

編輯2:我認為在指定路徑名時需要雙反斜杠。 我更新了我的代碼以反映這一點。 雙反斜杠代碼用於字符串中的常規反斜杠。

編輯:變量“newFileName”是否代表文件當前所在的路徑? 如果這個變量是別的,那么這可能是你的問題。 沒有看到更多的周圍代碼,我不確定。

我在代碼中一直使用相同的庫來生成.zips,但我從來沒有像你想要的那樣完成它。 我不知道為什么你的代碼會給你一個例外,但也許這會有效嗎? (假設你的字符串/路徑名都是正確的,而zip-library確實是導致問題的原因)

using (ZipFile zip = new ZipFile())
{
   zip.CompressionLevel = CompressionLevel.BestCompression;
   foreach (var entry in entries)
   {
      try
      {
         string newFileName = basefilename + Path.GetExtension(entry.FileName);
         zip.AddFile(newFileName, "");
      }
      catch (Exception) { }
   }
   zip.Save("c:\\files\\"+basefilename+ ".zip");
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM