将zip条目解压缩到另一个Zip文件

任何人都可以告诉我为什么以下代码不起作用? 我正在使用SharpZipLib API用于Zip流,最新版本DL今天从他们的网站。 我试图使用这个逻辑将一个zip文件的内容合并到另一个zip文件,而不必在磁盘上执行IO,因为预期的zip文件可能包含windows的保留文件名。 我尝试过多个不同的源和目标zip文件(那些包含保留名称和不包含的名称)。 代码不会抛出任何exception,如果在每次写入操作之前检查缓冲区,您可以看到它包含实际数据,但在整个操作完成后,目标zip文件的大小没有改变,您可以探索它确认没有新文件(代码应该添加的文件)实际上已添加到目标文件中。 🙁

public static void CopyToZip(string inArchive, string outArchive) { ZipOutputStream outStream = null; ZipInputStream inStream = null; try { outStream = new ZipOutputStream(File.OpenWrite(outArchive)); outStream.IsStreamOwner = false; inStream = new ZipInputStream(File.OpenRead(inArchive)); ZipEntry currentEntry = inStream.GetNextEntry(); while (currentEntry != null) { byte[] buffer = new byte[1024]; ZipEntry newEntry = new ZipEntry(currentEntry.Name); newEntry.Size = currentEntry.Size; newEntry.DateTime = currentEntry.DateTime; outStream.PutNextEntry(newEntry); int size = 0; while ((size = inStream.Read(buffer, 0, buffer.Length)) > 0) { outStream.Write(buffer, 0, size); } outStream.CloseEntry(); currentEntry = inStream.GetNextEntry(); } outStream.IsStreamOwner = true; } catch (Exception e) { throw e; } finally { try { outStream.Close(); } catch (Exception ignore) { } try { inStream.Close(); } catch (Exception ignore) { } } } 

我最终使用不同的API执行此操作。 来自http://dotnetzip.codeplex.com/的 DotNet zip。 这是实施:

  public static void CopyToZip(string inArchive, string outArchive, string tempPath) { ZipFile inZip = null; ZipFile outZip = null; try { inZip = new ZipFile(inArchive); outZip = new ZipFile(outArchive); List tempNames = new List(); List originalNames = new List(); int I = 0; foreach (ZipEntry entry in inZip) { if (!entry.IsDirectory) { string tempName = Path.Combine(tempPath, "tmp.tmp"); string oldName = entry.FileName; byte[] buffer = new byte[4026]; Stream inStream = null; FileStream stream = null; try { inStream = entry.OpenReader(); stream = new FileStream(tempName, FileMode.Create, FileAccess.ReadWrite); int size = 0; while ((size = inStream.Read(buffer, 0, buffer.Length)) > 0) { stream.Write(buffer, 0, size); } inStream.Close(); stream.Flush(); stream.Close(); inStream = new FileStream(tempName, FileMode.Open, FileAccess.Read); outZip.AddEntry(oldName, inStream); outZip.Save(); } catch (Exception exe) { throw exe; } finally { try { inStream.Close(); } catch (Exception ignore) { } try { stream.Close(); } catch (Exception ignore) { } } } } } catch (Exception e) { throw e; } } 

我看到的一个问题是你使用File.OpenWrite()打开输出zip文件,它将替换现有的输出文件而不是将新条目合并到其中。

SharpDevelop Wiki上有一个示例,它提供了使用内存流更新zip文件的示例。 它可以在http://wiki.sharpdevelop.net/SharpZipLib_Updating.ashx#Updating_a_zip_file_in_memory_1找到

以下是一些更简单的代码,它们将从输入zip读取并写入输出zip,这可能已经存在。 它不需要将临时数据写入文件系统。

  public static void CopyToZip(string inArchive, string outArchive) { using (inZip = new ZipFile(inArchive), outZip = new ZipFile(outArchive)) { Func> getInStreamReturner = (name) => { return new Func(a){ return inZip[a].OpenReader(); }; }; foreach (ZipEntry entry in inZip) { if (!entry.IsDirectory) { string zipEntryName = entry.FileName; outZip.AddEntry(zipEntryName, getInStreamReturner(zipEntryName), (name, stream) => stream.Close() ); } } outZip.Save(); } } 

笔记:

  1. 此方法使用ZipFile.AddEntry重载,该重载接受两个委托:一个开启器和一个闭合器。 这些函数ZipFile.Save时被ZipFile.Save 。 前委托需要打开并返回包含要压缩的数据的流。 后一个委托需要关闭流。

  2. 有必要定义getInStreamReturner Func,以便在getInStreamReturner打开正确的流。 请记住,每次循环时, zipEntryName更改值。 此外, ZipEntry.OpenReader()在实际的zip数据上打开一个流,该数据随着时间的推移进行读取和解压缩。 每个ZipFile,每次只能打开其中一个。 getInStreamReturner每次通过循环创建一个新函数,从而创建一个闭包来保留zipEntryName的值,以便在zipEntryName时引用。

  3. 如果inArchive和outArchive之间存在名称冲突,则此方法将失败。 为了避免这种情况,你需要检查它并以某种方式避免它。 设计一个新的,唯一的名称,或者跳过将具有重复名称的条目添加到outarchive中。

  4. 我没有测试过这个。


虽然这种方法不会写入文件系统,但它解压缩并重新压缩文件数据。 有一个开放式请求,要求DotNetZip在不进行解压缩/重新压缩跳转的情况下迁移条目。 我还没有实现。