Delphi’s TZipFile working on a stream

Recent versions of Delphi (for a suitable definition of “recent”) come with a TZipFile class implemented in the unit System.Zip. This class has the really neat feature that it can not only read the contents of a ZIP file directly from a stream but also extract a file from that stream to a TBytes array, thus it does not require any file system access.

E.g. imagine you have got a memory stream containing the contents of a ZIP archive (e.g. you got a zipped XML description from a GenICam compatible camera using the GCReadPort (PDF) function.). You now want to read a file from this stream and immediately process that file’s content without first writing it to the file system. This can be done like this:

var
  ms: TMemoryStream;
  ZIP TZipFile;
  Data: TBytes;
  // [...]
begin
  //
  // Some code here retrieves the ZIP archive's content and stores it in ms
  //
  Zip := TZipFile.Create;
  try
    ms.Position := 0;
    Zip.Open(ms, zmRead);
    if Zip.FileCount > 0 then begin
      Zip.Read(0, Data);
    end;
  finally
    FreeAndNil(Zip);
  end;
  //
  // now we have the file's content in Data and can process it.
  //
end;

One very important step here is to set the stream’s position to 0 before passing it to TZipFile.Open. If you forget that (guess who did) you will likely get an EZipException in TZipFile.Read with the error message ‘Invalid Zip Local Header signature’. The reason for this is that TZipFile.Open assumes that the current stream position is the first byte of the first file stored in the ZIP archive:

procedure TZipFile.Open(ZipFileStream: TStream; OpenMode: TZipMode);
begin
  // [...]
  FStream := ZipFileStream;
  FStartFileData := FStream.Position;
  // [...]
end;

If it isn’t, it will try to read the files starting from that position and will fail with the exception mentioned above.

If you want to discuss this article, you can do so in the corresponding post in the international Delphi Praxis forum.