I'd like to replace a file (= delete old and add new) in a zip archive with the Delphi XE2/XE3 standard System.Zip unit. But there are no replace/delete methods. Does anybody have an idea how it could be achieved without needing to extract all files and add them to a new archive?
I have this code, but it adds the "document.txt" once more if it's already present:
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;
Note: I used TPAbbrevia before (that did the job), but I'd like to use Delphi's Zip unit now. So please do not answer something like "use another library". Thank you.
I'd recommend Abbrevia because I'm biased :), you already know it, and it doesn't require any hacks. Barring that, here's your 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;
Usage:
ZipFile.Delete('document.txt');
ZipFile.Add(SS, 'document.txt');