DotNetZip从其他zip的子集创建zip [英] DotNetZip creating zip from subset of other zip

查看:72
本文介绍了DotNetZip从其他zip的子集创建zip的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个很大的zip文件,我需要将其拆分为多个zip文件.在我现在创建的方法中,我有一个List对象.

I have a big zipfile that I need to split in multiple zip files. In the method I'm now creating I have a List object.

这是我得到的代码:

 //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()调用时遇到的错误:

And this is the error I get at the save() call :

{Ionic.Zlib.ZlibException:Ionic.Zlib.InflateManager.Inflate(FlushType flush)处的状态错误(块类型无效)Ionic.Zlib.ZlibCodec.Inflate(FlushType flush)在Ionic.Zlib.ZlibBaseStream.Read(Byte []缓冲区,Int32偏移量,Int32Ionic.Zlib.DeflateStream.Read(count [] buffer,Int32 offset,Ionic.Crc.CrcCalculatorStream.Read(Byte []缓冲区的Int32计数),Int32偏移量,Int32计数)在Ionic.Zip.SharedUtilities.ReadWithRetry(Stream s,Byte [] buffer,Int32偏移量,Int32计数,字符串FileName)Ionic.Zip.ZipEntry._WriteEntryData(Stream)在Ionic.Zip.ZipFile.Save()处的Ionic.Zip.ZipEntry.Write(Stream)Ionic.Zip.ZipFile.Save(Stream outputStream)位于

{Ionic.Zlib.ZlibException: Bad state (invalid block type) at Ionic.Zlib.InflateManager.Inflate(FlushType flush) at Ionic.Zlib.ZlibCodec.Inflate(FlushType flush) at Ionic.Zlib.ZlibBaseStream.Read(Byte[] buffer, Int32 offset, Int32 count) at Ionic.Zlib.DeflateStream.Read(Byte[] buffer, Int32 offset, Int32 count) at Ionic.Crc.CrcCalculatorStream.Read(Byte[] buffer, Int32 offset, Int32 count) at Ionic.Zip.SharedUtilities.ReadWithRetry(Stream s, Byte[] buffer, Int32 offset, Int32 count, String FileName) at Ionic.Zip.ZipEntry._WriteEntryData(Stream s) at Ionic.Zip.ZipEntry.Write(Stream s) at Ionic.Zip.ZipFile.Save() at Ionic.Zip.ZipFile.Save(Stream outputStream) at

我在做什么错了?

推荐答案

这是您在做的错误:您在单个ZipFile实例中有多个对ZipEntry.OpenReader()的挂起调用.您最多只能有一个待处理的ZipEntry.OpenReader().

here's what you're doing wrong: You have multiple pending calls to ZipEntry.OpenReader() in a single ZipFile instance. You can have at most, only one pending ZipEntry.OpenReader().

原因如下:当您使用ZipFile.Read()或新的ZipFile()实例化给定的zip文件并传递现有文件的名称时,仅创建一个Stream对象.当您调用ZipEntry.OpenReader()时,它将在Stream对象中导致Seek(),以将文件指针移动到该特定条目的压缩字节流的开头.当您再次调用ZipEntry.OpenReader()时,它将导致另一个Seek()到达流中的其他位置.因此,通过添加条目并连续调用OpenReader(),可以重复调用Seek(),但是只有最后一个有效.流游标将放置在数据的开头,该数据对应于对ZipEntry.OpenReader()的最后一次调用的条目.

Here's why: There is only one Stream object created when you instantiate a given zip file with ZipFile.Read() or new ZipFile(), passing the name of an existing file. When you call ZipEntry.OpenReader() , it results in a Seek() in the Stream object, to move the file pointer to the beginning of the compressed bytestream for that particular entry. When you call ZipEntry.OpenReader() again, it results in another Seek() to a different location in the stream. So by adding entries and calling OpenReader() in succession, you are calling Seek() repeatedly, but only the last one will be valid. The stream cursor will be placed at the start of the data for the entry corresponding to the last call to ZipEntry.OpenReader().

要解决此问题:放弃您的方法.创建比现有zip文件条目少的新zip文件的最简单方法是:通过读取现有文件实例化ZipFile,然后删除不需要的条目,然后将ZipFile.Save()调用到新路径.

To fix it: Scrap your approach. The simplest way to create a new zipfile with fewer entries than an existing zip file is this: instantiate a ZipFile by reading the existing file, then remove the entries you don't want, then call ZipFile.Save() to a new path.

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文件.

EDIT
What this does at the time you call Save(): the library reads the raw, compressed data for the entries you have NOT removed from the filesystem file, and writes them into a new archive file. This is really fast because it does not decompress and recompress each entry in order to put it into the new, smaller zip file. Basically it reads slices of binary data out of the original zip file, and concatenates them together to form the new, smaller zip file.

要生成多个较小的文件,可以使用原始zip文件重复执行此操作;只需将以上内容包装成一个循环,然后更改要删除的文件以及新的较小档案的文件名即可.读取现有的zipfile也非常快.

To produce multiple smaller files, you can do this repeatedly with the original zip file; just wrap the above in a loop and vary the files you remove, and the filename of the new, smaller archive. Reading an existing zipfile is also pretty fast.

作为替代方案,您可以解压缩并提取每个条目,然后重新压缩并将条目写入新的zip文件中.那是很长的路要走,但是有可能.在这种情况下,对于要创建的每个较小的zipfile,将需要创建两个ZipFile实例.通过阅读原始的zip存档打开第一个.对于您要保留的每个条目,创建一个MemoryStream,从条目中提取内容到该MemoryStream中,并记住在mem流中调用Seek()以将游标重置在该存储流上.然后使用第二个ZipFile实例,调用AddEntry(),使用该MemoryStream作为添加条目的源.仅在第二个实例上调用ZipFile.Save().

As an alternative, you could decompress and extract each entry, and then recompress and write the entry into a new zip file. That is the long way around, but it is possible. In that case, for each smaller zipfile you want to create, you will need to create two ZipFile instances. Open the first one by reading the original zip archive. For each entry you want to keep, create a MemoryStream, extract content from an entry into that MemoryStream, and remember to call Seek() in the mem stream to reset the cursor on the memory stream. Then using the second ZipFile instance, call AddEntry(), using that MemoryStream as the source for the added entry. Call ZipFile.Save() only on the second instance.

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的每个条目进行解压缩和重新压缩,这效率低下且不必要.

This works, but it involves decompression and recompression of each entry that goes into the smaller zip, which is inefficient and unnecessary.

如果您不介意减压和重新压缩的效率低下,则可以采用另一种方法:致电

If you don't mind the inefficiency of the decompression and recompression, there's an alternative you can employ: call the ZipFile.AddEntry() overload that accepts opener and closer delegates. What this does is defer the call to OpenReader() til the time the entry is being written to the new, smaller zip file. The effect is that you have just one pending OpenReader() at a time.

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

它仍然是低效率的,因为每个条目都经过解压缩和重新压缩,但是效率较低.

It's still inefficient, because each entry gets decompressed and recompressed, but it's a little less inefficient.

这篇关于DotNetZip从其他zip的子集创建zip的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆