我有一个大的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();
Run Code Online (Sandbox Code Playgroud)
这是我在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");
}
Run Code Online (Sandbox Code Playgroud)
编辑
在调用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");
}
}
Run Code Online (Sandbox Code Playgroud)
这有效,但它涉及到每个条目的解压缩和再压缩,这些条目进入较小的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");
}
Run Code Online (Sandbox Code Playgroud)
它仍然效率低下,因为每个条目都经过解压缩和重新压缩,但效率低一点.
| 归档时间: |
|
| 查看次数: |
5354 次 |
| 最近记录: |