DotNetZip从其他zip的子集创建zip

本文关键字:zip 子集 创建 DotNetZip 其他 | 更新日期: 2023-09-27 18:12:49

我有一个大的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。zlibeexception: Ionic.Zlib.InflateManager的坏状态(无效块类型)。充气(FlushType flush)在Ionic.Zlib.ZlibCodec。充气(FlushType flush)在Ionic.Zlib.ZlibBaseStream。Read(Byte[] buffer, Int32 offset, Int32 .在ionic . zlib . deflateststream。读取(Byte[] buffer, Int32偏移量)Int32计数)在Ionic.Crc.CrcCalculatorStream。读(Byte[]缓冲区,Int32偏移量,Int32计数)atIonic.Zip.SharedUtilities。ReadWithRetry(Stream s, Byte[] buffer, Int32offset, Int32计数,字符串文件名)Ionic.Zip.ZipEntry。_WriteEntryData(Stream s) atIonic.Zip.ZipEntry。写(流)在Ionic.Zip.ZipFile.Save() atIonic.Zip.ZipFile。Save(Stream outputStream) at

我做错了什么?

DotNetZip从其他zip的子集创建zip

您做错了:您在单个ZipFile实例中对ZipEntry.OpenReader()有多个待挂调用。你最多只能有一个待挂的ZipEntry.OpenReader()。

原因如下:当您使用ZipFile. read()或new ZipFile()实例化给定的zip文件时,只创建一个流对象,传递现有文件的名称。当您调用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文件。这是一个漫长的过程,但这是可能的。在这种情况下,对于您想要创建的每个较小的zipfile,您将需要创建两个zipfile实例。通过阅读原始zip归档文件打开第一个文件。对于您想要保留的每个条目,创建一个MemoryStream,从一个条目中提取内容到MemoryStream,并记住在内存流中调用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"是否代表文件当前所在的路径?如果这个变量是别的东西,那可能就是你的问题了。没有看到更多的周围代码,我不确定。

我在我的代码中一直使用相同的库来制作.zip,但我从来没有像你那样做过。我不知道为什么你的代码给你一个异常,但也许这将工作代替?(假设您的字符串/路径名都是正确的,并且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");
}