我有一个应用程序,它创建一个包含某个目录中某些JPEG的zip文件。我使用此代码以便:
using (var outStream = new FileStream("Out2.zip", FileMode.Create))
{
using (var zipStream = new ZipOutputStream(outStream))
{
foreach (string pathname in pathnames)
{
byte[] buffer = File.ReadAllBytes(pathname);
ZipEntry entry = new ZipEntry(Path.GetFileName(pathname));
entry.DateTime = now;
zipStream.PutNextEntry(entry);
zipStream.Write(buffer, 0, buffer.Length);
}
}
}
当我打开文件e时,所有在Windows下运行良好。 G。使用WinRAR,提取文件。但是当我尝试在Mac OS X上解压缩我的存档时,它只会创建一个.cpgz
文件。很没用。
在Windows和Mac OS X上提取在Windows上使用相同文件手动创建的普通.zip
文件时没有任何问题。
我在互联网上找到了上面的代码,所以我不确定整件事情是否正确。我想知道是否需要使用zipStream.Write()
才能直接写入流?
答案 0 :(得分:16)
我不确定,因为我对SharpZipLib或OSX不是很熟悉,但我仍然可以为您提供一些有用的见解。
我花了一些时间浏览zip规范,实际上我写了DotNetZip,这是一个.NET的zip库,与SharpZipLib无关。
目前在DotNetZip的用户论坛上,正在讨论由DotNetZip生成的无法在OSX上读取的zip文件。其中一个使用该库的人遇到的问题与您所看到的类似。除了我不知道.cpgxz文件是什么。
我们跟踪了一下。此时最有希望的理论是OSX不喜欢每个zip条目标题中“通用位域”中的“位3”。
第3位并不新鲜。 PKWare在17年前为该规范添加了第3位。它旨在以SharpZipLib的工作方式支持流式媒体生成。 DotNetZip还有一种方法可以在流式传输时生成zip文件,如果以这种方式使用它也会在zip文件中设置bit-3,尽管通常DotNetZip会生成一个zip-3,其中包含3位未设置在里面。
从我们可以看出,当第3位设置时,OSX zip阅读器(不管它是什么 - 就像我说我不熟悉OSX)在zip文件上窒息。没有第3位的相同压缩内容允许打开zip文件。实际上它并不像仅仅翻转一位那么简单 - 位的存在表明存在其他元数据。所以我使用“第3位”作为所有这些的简写。
所以理论是第3位导致问题。我自己没有测试过。与拥有OSX机器的人的通信存在一些阻抗不匹配 - 因此尚未解决。
但是,如果这个理论成立,它可以解释你的情况:WinRar和任何Windows机器都可以打开文件,但OSX不能。
在DotNetZip论坛上,我们讨论了如何解决这个问题。尽管我可以说,OSX拉链读取器已损坏,无法处理第3位,因此解决方法是生成第3位未设置的zip文件。我不知道是否可以说服SharpZipLib这样做。
我知道如果您使用DotNetZip,并使用普通的ZipFile类,并保存到可搜索的流(如文件系统文件),您将获得一个没有第3位设置的zip。如果理论是正确的,那么每次都应该在Mac上打开没有任何问题。这是DotNetZip用户报告的结果。这只是一个结果,所以还没有推广,但看起来似乎有道理。
您的方案的示例代码:
using (ZipFile zip = new ZipFile()
{
zip.AddFiles(pathnames);
zip.Save("Out2.zip");
}
只是为了好奇,在DotNetZip中,如果您使用ZipFile类并将其保存到不可搜索的流(如ASPNET的Response.OutputStream),或者如果您使用DotNetZip中的ZipOutputStream类,那么您将获得第3位设置只是(没有回头)。 我认为SharpZipLib的ZipOutputStream也总是“仅向前”。
答案 1 :(得分:15)
今天遇到了完全相同的问题。我试图按照提议实现CRC的东西,但它没有帮助。
我最终在此页面找到了解决方案:http://community.sharpdevelop.net/forums/p/7957/23476.aspx#23476
结果,我只需在我的代码中添加这一行:
oZIPStream.UseZip64 = UseZip64.Off;
该文件在MacOS X上打开: - )
干杯 fred的
答案 2 :(得分:13)
所以,我搜索了一些关于如何使用SharpZipLib的例子,我终于让它在Windows和OS x上运行了。基本上我将文件的“Crc32”添加到zip存档中。不知道这是什么。
以下代码对我有用:
using (var outStream = new FileStream("Out3.zip", FileMode.Create))
{
using (var zipStream = new ZipOutputStream(outStream))
{
Crc32 crc = new Crc32();
foreach (string pathname in pathnames)
{
byte[] buffer = File.ReadAllBytes(pathname);
ZipEntry entry = new ZipEntry(Path.GetFileName(pathname));
entry.DateTime = now;
entry.Size = buffer.Length;
crc.Reset();
crc.Update(buffer);
entry.Crc = crc.Value;
zipStream.PutNextEntry(entry);
zipStream.Write(buffer, 0, buffer.Length);
}
zipStream.Finish();
// I dont think this is required at all
zipStream.Flush();
zipStream.Close();
}
}
cheeso的解释:
CRC是循环冗余校验 - 它是条目数据的校验和。通常,zip文件中每个条目的标题包含一堆元数据,包括在流式传输所有条目数据之前无法知道的一些内容 - CRC,未压缩大小和压缩大小。通过流输出生成zipfile时,zip规范允许设置一个位(第3位)以指定这三个数据字段将紧跟在条目数据之后。
如果使用ZipOutputStream,通常在写入条目数据时,会对其进行压缩并计算CRC,并在文件数据之后立即写入3个数据字段。
您所做的是将数据流式传输两次 - 这是第一次隐式地在您编写文件之前计算文件上的CRC。如果我的理论是正确的,那么正在发生的事情就是:当你在写入文件数据之前向zipStream提供CRC时,这允许CRC出现在条目标题中的正常位置,这使OSX保持高兴。我不确定其他两个量(压缩和未压缩的大小)会发生什么。
答案 3 :(得分:2)
我遇到了完全相同的问题,我的错误(以及您的示例代码中)我没有为每个条目提供文件长度。
示例代码:
...
ZipEntry entry = new ZipEntry(Path.GetFileName(pathname));
entry.DateTime = now;
var fileInfo = new FileInfo(pathname)
entry.size = fileInfo.lenght;
...
答案 4 :(得分:2)
我用反斜杠分隔文件夹名称...当我将其更改为正斜杠时,它有效!
答案 5 :(得分:1)
.cpgz
文件正在发生的事情是,Archive Utility是由扩展名为.zip
的文件启动的。 Archive Utility检查文件并认为它未被压缩,因此它正在压缩它。出于某些奇怪的原因,.cpgz
(CPIO归档+ gzip压缩)是默认设置。您可以在Archive Utility的首选项中设置不同的默认值。
如果您确实发现这是OS X的zip解码器的问题,请提交bug。您也可以尝试使用ditto
命令行工具将其解压缩;您可能会收到更好的错误消息。当然,OS X还提供了unzip
,Info-ZIP实用程序,但我希望它可以工作。
答案 6 :(得分:0)
我同意Cheeso的回答,但如果输入文件大小大于2GB,则byte [] buffer = File.ReadAllBytes(pathname);将抛出IO异常。 所以我修改了Cheeso代码,它就像所有文件的魅力一样。
long maxDataToBuffer = 104857600;//100MB
using (var outStream = new FileStream("Out3.zip", FileMode.Create))
{
using (var zipStream = new ZipOutputStream(outStream))
{
Crc32 crc = new Crc32();
foreach (string pathname in pathnames)
{
tempBuffLength = maxDataToBuffer;
FileStream fs = System.IO.File.OpenRead(pathname);
ZipEntry entry = new ZipEntry(Path.GetFileName(pathname));
entry.DateTime = now;
entry.Size = buffer.Length;
crc.Reset();
long totalBuffLength = 0;
if (fs.Length <= tempBuffLength) tempBuffLength = fs.Length;
byte[] buffer = null;
while (totalBuffLength < fs.Length)
{
if ((fs.Length - totalBuffLength) <= tempBuffLength)
tempBuffLength = (fs.Length - totalBuffLength);
totalBuffLength += tempBuffLength;
buffer = new byte[tempBuffLength];
fs.Read(buffer, 0, buffer.Length);
crc.Update(buffer, 0, buffer.Length);
buffer = null;
}
entry.Crc = crc.Value;
zipStream.PutNextEntry(entry);
tempBuffLength = maxDataToBuffer;
fs = System.IO.File.OpenRead(pathname);
totalBuffLength = 0;
if (fs.Length <= tempBuffLength) tempBuffLength = fs.Length;
buffer = null;
while (totalBuffLength < fs.Length)
{
if ((fs.Length - totalBuffLength) <= tempBuffLength)
tempBuffLength = (fs.Length - totalBuffLength);
totalBuffLength += tempBuffLength;
buffer = new byte[tempBuffLength];
fs.Read(buffer, 0, buffer.Length);
zipStream.Write(buffer, 0, buffer.Length);
buffer = null;
}
fs.Close();
}
zipStream.Finish();
// I dont think this is required at all
zipStream.Flush();
zipStream.Close();
}
}
答案 7 :(得分:0)
我遇到了类似的问题但是在Windows 7上。我更新到了撰写最新版本的ICSharpZipLib 0.86.0.518。从那时起,我再也无法解压缩使用到目前为止工作的代码创建的任何ZIP存档。
根据我尝试提取的工具,错误消息有所不同:
诀窍是删除CRC计算,如下所述:http://community.sharpdevelop.net/forums/t/8630.aspx
所以我删除了以下行:
entry.Crc = crc.Value
从那时起,我可以再次使用任何第三方工具解压缩ZIP存档。我希望这有助于某人。
答案 8 :(得分:0)
有两件事:
确保您的基础输出流是可搜索的,或者SharpZipLib无法备份并填写您省略的任何ZipEntry字段(大小,crc,压缩大小......)。因此,SharpZipLib将强制推进第3和第3位;要启用。在以前的答案中已经很好地解释了背景。
填写ZipEntry.Size,或明确设置stream.UseZip64 = UseZip64.Off。默认情况下保守地假设流可能非常大。解压缩然后需要&#34; pk 4.5&#34;支持。
答案 9 :(得分:0)
当存档为空(其中没有条目)时,我遇到了奇怪的行为,它无法在MAC上打开 - 仅生成cpgz。我们的想法是在其中放入一个虚拟的.txt文件,以防没有归档文件。