Delphi: open a zip archive from a stream -> extract to a stream

13,612

The zip file component that is built into XE2 will do this.

There is an overloaded Open method that receives a TStream as its input parameters.

To extract individual files you can call an overloaded Read method passing the name of the file that you wish to extract. The extracted file is returned as a new instance of TStream. You can that use CopyFrom on that instance to transfer the extracted file to your stream.

var
  ZipFile: TZipFile;
  DownloadedStream, DecompressionStream, MyStream: TStream;
  LocalHeader: TZipHeader;
...
ZipFile := TZipFile.Create;
try
  ZipFile.Open(DownloadedStream, zmRead);
  ZipFile.Read('myzippedfile', DecompressionStream, LocalHeader);
  try
    MyStream.CopyFrom(DecompressionStream, DecompressionStream.Size);
  finally
    DecompressionStream.Free;
  end;
finally
  ZipFile.Free;
end;

Note that I've not tested this code, I've just written it based on the source code for TZipFile and the documentation contained in that source code. There may be a few wrinkles in this but if the code behaves as advertised it meets your needs perfectly.


OK, now I tested it because I was curious. Here's the program that shows that this all works as advertised:

program ZipTest;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Classes,
  System.Zip;

procedure ExtractToFile(
  const ZipFileName: string;
  const ZippedFileIndex: Integer;
  const ExtractedFileName: string
);
var
  ZipFile: TZipFile;
  DownloadedStream, DecompressionStream, OutputStream: TStream;
  LocalHeader: TZipHeader;
begin
  DownloadedStream := TFileStream.Create(ZipFileName, fmOpenRead);
  try
    ZipFile := TZipFile.Create;
    try
      ZipFile.Open(DownloadedStream, zmRead);
      ZipFile.Read(ZippedFileIndex, DecompressionStream, LocalHeader);
      try
        OutputStream := TFileStream.Create(ExtractedFileName, fmCreate);
        try
          OutputStream.CopyFrom(DecompressionStream, DecompressionStream.Size);
        finally
          OutputStream.Free;
        end;
      finally
        DecompressionStream.Free;
      end;
    finally
      ZipFile.Free;
    end;
  finally
    DownloadedStream.Free;
  end;
end;

begin
  try
    ExtractToFile('C:\desktop\test.zip', 0, 'C:\desktop\out.txt');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Note that I extracted by index rather than file name since that was more convenient for me. And I used file streams rather than memory streams which I imagine you would use. However, since the TZipFile methods work with TStream I'm sure that the code will work with streams of any form.


This is the latest in a series of questions about ZIP files. I know that you are using XE2 and I wonder why you seem reluctant to use the built in ZIP class that XE2 provides. I've not seen anything to indicate that it will not fulfil your requirements. In fact, it is precisely this ability to work directly with streams that makes me feel it has sufficient generality for any application.

Share:
13,612
maxfax
Author by

maxfax

Updated on June 17, 2022

Comments

  • maxfax
    maxfax over 1 year

    Are there any zip components with such features? I need to download a zip archive from the Internet to a stream, then to open the archive from the stream and then to extract files to another stream.

    E.g. ZipForge can open an archive from a stream ZipForge.OpenArchive(MyStream, false); but how to extract to another one...?

    procedure ExtractToStream(FileName: WideString; Stream: TStream); 
    

    Description

    Use ExtractToStream to decompress data stored in the file inside the archive to a TStream descendant object like TFileStream, TMemoryStream or TBlobStream.

    The FileName parameter specifies file name being extracted.

    And what use of the OpenArchive(MyStream, false) method if extraction isn't supported...

  • maxfax
    maxfax almost 12 years
    I didn't see the Read method (I checked fast), there is pure information about it docwiki.embarcadero.com/VCL/en/System.Zip.TZipFile.Read
  • David Heffernan
    David Heffernan almost 12 years
    @maxfax What edition of Delphi do you have? I've got Pro and that comes with the source. My answers to both your recent ZIP questions are based entirely on reading the source. I know precisely nothing about ZIP files and nothing about this component. If you have the source code then that will be a huge help to you I think.
  • splash
    splash over 9 years
    Just successfully removed ZipForge dependency thanks to your example. :)
  • quickly_now
    quickly_now about 8 years
    Just a small note on this - it looks (in XE8) like anything put into the ZIP using the STORE method will have the wrong size if you attempt to extract it using the.Read() to a stream. As far as I can tell, this is reported (QC 115388) but QC is down at the time of entering this note. For me this is a killer, and I'll have to use a different Zip package.
  • campbell.rw
    campbell.rw about 8 years
    This doesn't doesn't work as expected for me when trying to unzip a zip file that contains subdirs. It just produces a single file with the same name as my destination directory.
  • David Heffernan
    David Heffernan about 8 years
    That sounds like a new question. Make sure you include a minimal reproducible example