【问题标题】:Delphi XE2 TZipFile: replace a file in zip archiveDelphi XE2 TZipFile:替换zip存档中的文件
【发布时间】:2012-10-31 18:21:43
【问题描述】:

我想用 Delphi XE2/XE3 标准 System.Zip 单元替换 zip 存档中的文件(= 删除旧文件并添加新文件)。但是没有替换/删除方法。有没有人知道如何在不需要提取所有文件并将它们添加到新存档的情况下实现它?

我有这段代码,但如果它已经存在,它会再次添加“document.txt”:

var
  ZipFile: TZipFile;
  SS: TStringStream;
const
  ZipDocument = 'E:\document.zip';
begin
  ZipFile := TZipFile.Create; //Zipfile: TZipFile
  SS := TStringStream.Create('hello');
  try
    if FileExists(ZipDocument) then
      ZipFile.Open(ZipDocument, zmReadWrite)
    else
      ZipFile.Open(ZipDocument, zmWrite);

    ZipFile.Add(SS, 'document.txt');

    ZipFile.Close;
  finally
    SS.Free;
    ZipFile.Free;
  end;
end;

注意:我以前使用过 TPAbbrevia(完成了这项工作),但我现在想使用 Delphi 的 Zip 单元。所以请不要回答“使用另一个库”之类的问题。谢谢。

【问题讨论】:

  • 您已经回答了自己的问题。内置的 ZIP 库不支持该功能。
  • 也许有人写了一个hack?
  • 你为什么不使用 Abbrevia?有人告诉我,它非常好。
  • 我确实使用过它,它很棒。但是我编写了一个需要 zip 功能的库,并且我确实想减少依赖关系。此外,如果您需要 OSX 平台支持,对于没有经验的用户来说,安装 TPAbbrevia 有点棘手。

标签: delphi zip delphi-xe2 delphi-xe3


【解决方案1】:

我会推荐 Abbrevia,因为我有偏见 :),你已经知道了,而且它不需要任何技巧。除此之外,这是你的 hack:

type
  TZipFileHelper = class helper for TZipFile
    procedure Delete(FileName: string);
  end;

{ TZipFileHelper }

procedure TZipFileHelper.Delete(FileName: string);
var
  i, j: Integer;
  StartOffset, EndOffset, Size: UInt32;
  Header: TZipHeader;
  Buf: TBytes;
begin
  i := IndexOf(FileName);
  if i <> -1 then begin
    // Find extents for existing file in the file stream
    StartOffset := Self.FFiles[i].LocalHeaderOffset;
    EndOffset := Self.FEndFileData;
    for j := 0 to Self.FFiles.Count - 1 do begin
      if (Self.FFiles[j].LocalHeaderOffset > StartOffset) and
         (Self.FFiles[j].LocalHeaderOffset <= EndOffset) then
        EndOffset := Self.FFiles[j].LocalHeaderOffset;
    end;
    Size := EndOffset - StartOffset;
    // Update central directory header data
    Self.FFiles.Delete(i);
    for j := 0 to Self.FFiles.Count - 1 do begin
      Header := Self.FFiles[j];
      if Header.LocalHeaderOffset > StartOffset then begin
        Header.LocalHeaderOffset := Header.LocalHeaderOffset - Size;
        Self.FFiles[j] := Header;
      end;
    end;
    // Remove existing file stream
    SetLength(Buf, Self.FEndFileData - EndOffset);
    Self.FStream.Position := EndOffset;
    if Length(Buf) > 0 then
      Self.FStream.Read(Buf[0], Length(Buf));
    Self.FStream.Size := StartOffset;
    if Length(Buf) > 0 then
      Self.FStream.Write(Buf[0], Length(Buf));
    Self.FEndFileData := Self.FStream.Position;
  end;
end;

用法:

ZipFile.Delete('document.txt');
ZipFile.Add(SS, 'document.txt');

【讨论】:

  • +1 该黑客实际上是从 ZIP 中删除旧文件,还是只是将其从文件表或其他名称中删除?
  • 不,它不会物理删除文件(zip 文件会增长),但这是一个好的开始。我将尝试扩展代码并从 FStream 中删除旧文件并重新计算标题(新文件位置)。
  • @David 不错;我应该仔细看看实现。更新了更完整的 hack,确实删除了文件内容。
  • @CraigPeterson 这样更好。对于非常大的文件,您可能不想一次将所有尾随流加载到内存中。但我确信 oxo 可以担心这样的细节。
  • @CraigPeterson 比我快 :) 非常感谢!就我的目的而言,尾随流的大小不是问题(我不希望有大文件),但是用于复制数据的额外循环对任何人来说绝对不是问题。克雷格已经完成了困难的部分。
猜你喜欢
  • 1970-01-01
  • 2012-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-10
  • 2012-08-08
相关资源
最近更新 更多