gpt4 book ai didi

c# - DotNetZip 从其他 zip 的子集创建 zip

转载 作者:行者123 更新时间:2023-11-30 13:49:56 28 4
gpt4 key购买 nike

我有一个很大的 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: 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()。

原因如下:当您使用 ZipFile.Read() 或 new 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 文件。这是很长的路要走,但这是可能的。在这种情况下,对于要创建的每个较小的 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 的每个条目的解压缩和重新压缩,这是低效且不必要的。


如果您不介意解压缩和重新压缩的低效率,您可以使用另一种方法:调用 the ZipFile.AddEntry() overload that accepts opener and closer delegates .这样做是将对 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");
}

它仍然是低效的,因为每个条目都会被解压缩和重新压缩,但它的效率要低一些。

关于c# - DotNetZip 从其他 zip 的子集创建 zip,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7959211/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com