Delphi: открыть zip-архив из потока -> извлечь в поток - PullRequest
7 голосов
/ 17 января 2012

Есть ли компоненты zip с такими функциями? Мне нужно загрузить zip-архив из Интернета в поток, затем открыть архив из потока и затем извлечь файлы в другой поток.

например. ZipForge может открыть архив из потока ZipForge.OpenArchive(MyStream, false); но как извлечь в другой ...?

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

Описание

Используйте ExtractToStream для распаковки данных, хранящихся в файле внутри архивировать в объект-потомок TStream, такой как TFileStream, TMemoryStream или TBlobStream.

Параметр FileName указывает имя извлекаемого файла.

А какой метод OpenArchive(MyStream, false) используется, если извлечение не поддерживается ...

1 Ответ

22 голосов
/ 17 января 2012

Компонент zip-файла, встроенный в XE2, сделает это.

Существует перегруженный метод Open, который получает TStream в качестве входных параметров.

Для извлечения отдельных файлов вы можете вызвать перегруженный метод Read, передав имя файла, который вы хотите извлечь. Извлеченный файл возвращается как новый экземпляр TStream. Вы можете использовать CopyFrom в этом экземпляре для передачи извлеченного файла в ваш поток.

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;

Обратите внимание, что я не тестировал этот код, я просто написал его на основе исходного кода для TZipFile и документации, содержащейся в этом исходном коде. В этом может быть несколько складок, но если код ведет себя так, как объявлено, он полностью соответствует вашим потребностям.


Хорошо, теперь я проверил это, потому что мне было любопытно. Вот программа, которая показывает, что все это работает так, как рекламируется:

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.

Обратите внимание, что я извлекал по индексу, а не по имени файла, так как это было более удобно для меня. И я использовал потоки файлов, а не потоки памяти, которые, я полагаю, вы бы использовали. Однако, поскольку методы TZipFile работают с TStream, я уверен, что код будет работать с потоками любой формы.


Это последний из серии вопросов о ZIP-файлах. Я знаю, что вы используете XE2, и мне интересно, почему вы, похоже, неохотно используете встроенный класс ZIP, который предоставляет XE2. Я не видел ничего, что указывало бы на то, что оно не будет соответствовать вашим требованиям. Фактически, именно эта способность работать напрямую с потоками позволяет мне чувствовать, что она имеет достаточную общность для любого приложения.

...