Как работать с данными, закодированными с помощью gzip, в Delphi?

Кажется, я не могу понять, как обращаться с данными, закодированными с помощью gzip, в Delphi. Я создал пример приложения:

program GetRequestWithGzip;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Classes,
  System.Net.HttpClient,
  System.Net.URLClient,
  System.NetConsts;

var
  Client: TNetHTTPClient;
  Response: IHTTPResponse;
  Stream: TMemoryStream;
  HttpHeaders: TNetHeaders;

begin
  try
    Client := TNetHTTPClient.Create(nil);
    try
      // Set the Accept-Encoding header
      SetLength(HttpHeaders, 1);
      HttpHeaders[0].Name := 'Accept-Encoding';
      HttpHeaders[0].Value := 'gzip, deflate';

      // Prepare a stream for the response
      Stream := TMemoryStream.Create;
      try
        // Perform the GET request
        Response := Client.Get('https://www.google.com', Stream, HttpHeaders);

        // Check if the response was gzip-encoded
        if Response.ContentEncoding = 'gzip' then
        begin
          Writeln('Response is gzip encoded. Please decompress to read.');
          // Here, you would add your gzip decompression logic
        end
        else
        begin
          // If the response is not encoded, simply output the response
          Stream.Position := 0; // Reset stream position to read from the beginning
          Writeln('Response received: ', Stream.Size, ' bytes');
          // Further processing of Stream as needed
        end;
      finally
        Stream.Free;
      end;
    finally
      Client.Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  Writeln('Press Enter to exit.');
  Readln;
end.

Когда я работаю с этим вот так, я всегда получаю всего 10 байтов, указывающих, что у меня есть заголовок:

unit TGZDecompression;

interface

uses
  System.SysUtils, System.Classes, System.ZLib;

function DecompressGzipStream(const Input: TStream): TBytes;

implementation

type
  TGzipHeader = packed record
    ID1, ID2, CM, FLG: Byte;
    MTIME: Cardinal;
    XFL, OS: Byte;
  end;

  TGzipFlag = (FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT);
  TGzipFlags = set of TGzipFlag;

procedure SkipGzipHeader(const Stream: TStream);
var
  Header: TGzipHeader;
  Flags: TGzipFlags;
  XLEN: Word;
  B: Byte;
begin
  Stream.Read(Header, SizeOf(Header));
  if (Header.ID1 <> $1F) or (Header.ID2 <> $8B) then
    raise Exception.Create('Not a valid gzip stream');

  Flags := TGzipFlags(Byte(Header.FLG));

  if FEXTRA in Flags then
  begin
    Stream.Read(XLEN, SizeOf(XLEN));
    Stream.Seek(XLEN, soCurrent); // Skip the extra fields
  end;

  if FNAME in Flags then
    repeat
      Stream.Read(B, 1);
    until B = 0;

  if FCOMMENT in Flags then
    repeat
      Stream.Read(B, 1);
    until B = 0;

  if FHCRC in Flags then
    Stream.Seek(2, soCurrent); // Skip the CRC16 for the header
end;

function DecompressGzipStream(const Input: TStream): TBytes;
var
  DecompressionStream: TDecompressionStream;
  MemoryStream: TMemoryStream;
begin
  SkipGzipHeader(Input);
  DecompressionStream := TDecompressionStream.Create(Input, False); // Do not own the stream
  MemoryStream := TMemoryStream.Create;
  try
    MemoryStream.CopyFrom(DecompressionStream, 0);
    SetLength(Result, MemoryStream.Size);
    MemoryStream.Position := 0;
    MemoryStream.Read(Result, MemoryStream.Size);
  finally
    DecompressionStream.Free;
    MemoryStream.Free;
  end;
end;

end.

Заголовок имеет следующие значения в окне просмотра:

ID1=31
ID2=139
CM=8
FLG=0
TMIME=0
XFL=2
OS=255

Поскольку флаги не установлены, заголовки не пересылают поток дальше 10 байт. Когда я звоню на линию:

MemoryStream.CopyFrom(DecompressionStream, 0);

который распакует данные, которые я получаю:

Исключение первого шанса по адресу $00007FFFBF9753AC. Класс исключения EZDecompressionError с сообщением «ошибка данных».

Я просмотрел документацию Delphi и не нашел класса данных GZIP, который мог бы мне помочь. Нужно ли мне создавать его в 2024 году?

Я использую ZLib*.pas TheLazyTomcat вместе с двоичным файлом zlib.dll, предоставленным им для zlib 1.2.13 в целом, а также для gzip, который там просто рассматривается как другой тип потока. До сих пор я мог без проблем расшифровать ответы как deflate, так и gzip.

AmigoJack 03.04.2024 23:43

Хорошее объяснение здесь: stackoverflow.com/a/22311297

FredS 03.04.2024 23:45

Я не слишком хорошо знаком с TNetHTTPClient, но поскольку это всего лишь оболочка для HTTP-клиента, предоставляемого ОС, разве он не поддерживает автоматическую распаковку сжатого ответа?

Remy Lebeau 04.04.2024 02:18

Возможно, вы могли бы попробовать использовать 7z.dll для распаковки данных. Существует множество заголовков Delphi для 7z.dll.

dwrbudr 04.04.2024 05:30
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
179
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Похоже, вам не хватает аргумента в TDecompressionStream.Create. Вы хотите TDecompressionStream.Create(Input. -15, False) распаковать необработанный сдутый файл. Или вы можете просто исключить всю обработку заголовка gzip и позволить декомпрессору справиться с этим с помощью TDecompressionStream.Create(Input, 15+32, False). Это также автоматически распакует поток zlib, если он встречается вместо потока gzip. (Вы принимаете deflate так же, как и gzip.)

Благодаря Марку Адлеру, я просмотрел документацию, но мой поиск не нашел ее, должно быть, я искал не ту справку.

В приведенном ниже коде показан ответ Марка для всех, кто любит его копировать:

function DecompressGzipStream(const Input: TStream): TBytes;
var
  DecompressionStream: TDecompressionStream;
  MemoryStream: TMemoryStream;
begin
  // SkipGzipHeader(Input);
  DecompressionStream := TDecompressionStream.Create(Input, 15 + 32, False);
  MemoryStream := TMemoryStream.Create;
  try
    MemoryStream.CopyFrom(DecompressionStream, 0);
    SetLength(Result, MemoryStream.Size);
    MemoryStream.Position := 0;
    MemoryStream.Read(Result, MemoryStream.Size);
  finally
    DecompressionStream.Free;
    MemoryStream.Free;
  end;
end;

В справке говорится:

Примечание. При распаковке файлов добавление к 32 указывает на автоматическое определение заголовков форматов zlib или gzip. Дополнительную информацию см. в разделе «Расширенные функции» Руководства по zlib.

Или через «Справка» > «Справка Delphi» > «Справка системной библиотеки» > System.ZLib.TZDecompressionStream.Create:

Delphi IDE window menu to open the help

Finding System.ZLib.TZDecompressionStream.Create in the help file

"текст помощи" откуда? Пожалуйста, дайте ссылку на него.

AmigoJack 04.04.2024 09:27

@AmigoJack, я обновил для вас ответ по онлайн-ссылке: docwiki.embarcadero.com/Libraries/Athens/en/…

Walter Verhoeven 04.04.2024 10:03

О, это не для меня одного - это для всех, кто готов ответить. Если вы цитируете тексты, обратите внимание на ссылки и форматирование в них, иначе смысл и ссылки могут быть упущены.

AmigoJack 04.04.2024 13:58

Другие вопросы по теме