使用SharpZipLib创建的ZIP文件无法在Mac OS X上打开

时间:2009-11-05 11:34:48

标签: c# macos zip

唉,今天是愚蠢问题的日子,我是个白痴。

我有一个应用程序,它创建一个包含某个目录中某些JPEG的zip文件。我使用此代码以便:

  • 读取目录中的所有文件
  • 将每个人附加到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()才能直接写入流?

10 个答案:

答案 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存档。

根据我尝试提取的工具,错误消息有所不同:

  • 未知的压缩方法。
  • 本地标题中的压缩大小与新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文件,以防没有归档文件。