将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(); } }
笔记:
-
此方法使用
ZipFile.AddEntry
重载,该重载接受两个委托:一个开启器和一个闭合器。 这些函数在ZipFile.Save
时被ZipFile.Save
。 前委托需要打开并返回包含要压缩的数据的流。 后一个委托需要关闭流。 -
有必要定义
getInStreamReturner
Func,以便在getInStreamReturner
打开正确的流。 请记住,每次循环时,zipEntryName
更改值。 此外,ZipEntry.OpenReader()
在实际的zip数据上打开一个流,该数据随着时间的推移进行读取和解压缩。 每个ZipFile,每次只能打开其中一个。getInStreamReturner
每次通过循环创建一个新函数,从而创建一个闭包来保留zipEntryName
的值,以便在zipEntryName
时引用。 -
如果inArchive和outArchive之间存在名称冲突,则此方法将失败。 为了避免这种情况,你需要检查它并以某种方式避免它。 设计一个新的,唯一的名称,或者跳过将具有重复名称的条目添加到outarchive中。
-
我没有测试过这个。
虽然这种方法不会写入文件系统,但它会解压缩并重新压缩文件数据。 有一个开放式请求,要求DotNetZip在不进行解压缩/重新压缩跳转的情况下迁移条目。 我还没有实现。