【问题标题】:Delphi TZipFile extracts zero byte filesDelphi TZipFile 提取零字节文件
【发布时间】:2017-04-12 14:27:05
【问题描述】:

使用 Delphi XE2 和本机 TZipFile,我尝试提取下载的 zip 文件(包含 2 个压缩的 XML 文件)的内容,它总是提取零字节文件。

文件正在被 C# 代码压缩,如下所示:

var zipFile = new ZipFile();
foreach (Tuple<string, string> t in filesMeta) {
  zipFile.AddFile(string.Format("{0}{1}", StaticVariables.WebServerFileStorage, t.Item2), "").FileName = t.Item1 + ".xml";
}
response.Clear();
response.ContentType = "application/zip";
zipFile.Save(response.OutputStream);
response.End();

Delphi提取代码是这样的:

zipFile := TZipFile.Create;
try
  filename := 'C:\test\57f52480-ec87-4169-a820-0a65bc4ad952.zip';
  if zipFile.IsValid(filename) then begin
    zipFile.Open(filename, zmRead);
    zipFile.ExtractAll('C:\test\');
  end;
finally
  zipFile.Free;
end;

我什至尝试使用 TStream 作为源而不是磁盘上的文件。这实际上就是我想要做的,因为 zip 文件是从 Web 服务器下载到 TStream 中的。我尝试使用 TZipFile 的重载 open 方法来提取数据以打开流。

这得到了零字节文件,所以我将 zip 文件保存到磁盘并尝试从磁盘打开文件并解压缩。提取相同的零字节文件。

我什至尝试使用类方法从磁盘上的 zip 文件中提取文件:

System.Zip.TZipFile.ExtractZipFile(filename, 'C:\Test\');

提取相同的零字节文件。

zip 文件有效,Windows 7 原生文件处理和 7-Zip 都可以正确提取 2 个压缩的 XML 文件。

现在这里有点疯狂...

无奈之下,我试图看看 ExtractToFile() 过程是什么 David Heffernan 在这个问题中提出了关于 extracting a zip to a stream 的问题,所以我尝试像这样使用它:

var x : integer;
var fileCount : integer;
var fileNames : TArray<string>;
begin
  zipFile := TZipFile.Create;
  try
    filename := 'C:\test\57f52480-ec87-4169-a820-0a65bc4ad952.zip';
    if zipFile.IsValid(filename) then begin
      zipFile.Open(filename, zmRead);
      fileCount := zipFile.FileCount;
      fileNames := copy(zipFile.FileNames, 0, MaxInt);
      zipFile.Close;

      for x := 0 to fileCount-1 do begin
        // Use David Heffernan's stream procedure
        ExtractToFile(filename, x, 'C:\test\' + fileNames[x]);
      end;
    end;
  finally
    zipFile.Free;
  end;
end;

David 的程序按预期将文件提取到磁盘!什么鬼???

我真的很困惑为什么复杂的提取方法会起作用而简单的提取方法不起作用。如果必须,我会使用 David 的示例,但如果可能的话,我更希望正常提取工作。

感谢任何想法。

干杯! TJ

【问题讨论】:

  • 我无法重现这个。除非我们能重现它,否则它会很困难。我们可能也需要您的 ZIP 文件。你是如何创建它的?
  • @David - 该文件是使用上面问题中显示的 C# 代码在 Web 服务器上创建的。文件被拉出一些存储区域并压缩。不是我的代码,而是我从编写它的人那里得到的,以便将其包含在此处。如果我需要提供示例 zip 文件,我可以这样做。
  • 我认为这是必要的。通过尝试提取您在资源管理器中制作的简单 zip 文件来说服自己。然后弄清楚它们有何不同。
  • @David - 好吧,您可能正在做某事。我使用 Windows 资源管理器将两个 XML 文件压缩到我的驱动器上,并且该文件解压缩得很好。我查看了 zipFile.Read() 方法的标头,对于失败的文件,CompressedSize 和 UncompressedSize 都为零。我似乎记得看过一篇关于那个的帖子,但现在找不到了。在 Explorer 或 7-Zip 中压缩的所有文件的这些大小都不为零。
  • 你能检查一下标志字段的第 3 位是否设置在本地标头中吗?这不适用于 TZipFile。

标签: delphi zip delphi-xe2 zipfile


【解决方案1】:

TL;DR:我的问题的解决方案是使用外部组件。 Zip Forge(或简称)

继续阅读了解详情。

除了保存文件并使用 David 的函数重新打开它的迂回方式外,我没有尝试任何方法。虽然这样可行,但这并不是最佳选择,因为它需要我先将下载的文件保存到磁盘并重新打开它以进行提取,然后删除 zip 文件。我的目标是打开下载的流并将文件直接提取到磁盘。一次写入磁盘,没有临时文件。

我们甚至尝试了两个不同的 C# 库来压缩文件,并且都在流数据上给出了相同的结果。 Delphi TZipFile 组件无法处理它。

事实证明,我们有一个 ZipForge 的许可证,我已经忘记了它,因为我已经很久没有使用它了,它可以处理来自 C# Web 服务器的下载流并成功提取文件。

作为参考,我还尝试了 Abbrevia 组件版本 5.2,它也成功地从流中提取了文件。

希望这对其他人有帮助。

感谢 David 和 Uwe 的所有建议。

干杯!

TJ

【讨论】:

    【解决方案2】:

    我也遇到了同样的问题。

    TZipFile 的源代码显示,传递给 Read 函数的 TStream 返回整个 zip 文件,其位置设置为您想要的文件名的开头。所以不要倒带。对于 TZipHeader 中给出的未压缩长度,只需从 TStream 复制或执行您想要的操作。

    ZipStream := TStream.Create;
    ZipFile.Read(MyFileName, ZipStream, ZipHeader);
    //leave ZipStream pointer where it is!!!
    SomethingElse.LoadFromStream(ZipStream, ZipHeader.UncompressedSize);
    ZipStream.Free;
    

    在我看来,TZipFile 应该只使用请求的内容加载 ZipStream。如果不先浏览 TZipFile 源代码,这种实现方式并不直观。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-16
      • 2016-08-17
      • 2012-12-01
      • 2013-12-22
      • 1970-01-01
      • 2014-04-24
      • 1970-01-01
      相关资源
      最近更新 更多