уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Раз пошла такая тема, то сегодня речь пойдет про ещё один способ экономии трафика и повышении скорости работы с HTTP при использовании Synapse в Delphi. О том, как сэкономить немного трафика при скачивании web-страничек из Сети я рассказывал, когда рассматривал события сокета в Synapse, но этот способ выгодно применять кода необходимые нам данные находятся как можно ближе к началу страницы – тогда экономия есть и прирост скорости работы тоже наблюдается. Но использование события сокета не дает никакого эффекта, когда надо скачать страницу целиком. И в этом случае на помощь нам приходит использование GZip. При использовании GZip с Synapse мы имеем возможность сэкономить до 75% и более трафика, а соответственно и значительно ускорить работу наших программ для работы с Сетью.

Прежде, чем перейдем к рассмотрению примеров использования GZip в Synapse, рассмотрим несколько общих моментов по использованию сжатия в принципе.

Как указать серверу, что мы хотим получать сжатые данные?

Всё “общение” клиента и сервера на предмет того, что ожидает клиент, какие возможности поддерживаем и т.д. осуществляется с помощью заголовков. Пример использования заголовков я рассматривал в посте «Работа с байтовыми диапазонами в Synapse.«, в принципе, аналогичным образом мы можем сказать серверу, что хотим получить данные сжатые с помощью GZip. Для этого используется следующий заголовок:

Accept-Encoding: gzip,deflate

Что касается версии протокола HTTP, то немного покопавшись в Сети на тему какая из версий протоколов поддерживает GZip сжатие, нашел следующую информацию: nginx не отдает gzip если протокол HTTP/1.0, даже если его об этом просят (в заголовках). Для этого необходимо настроить опцию gzip_http_version, указав минимальную версию протокола 1.0, после чего сервер будет отдавать gzip по протоколу 1.0.

Таким образом, если сервер поддерживает GZip сжатие, то для надежности лучше всего у THTTPSend задавать следующие значения:

HTTPSend.Headers.Add('Accept-Encoding: gzip,deflate');//заголовок
HTTPSend.Protocol:='1.1';//по умолчанию выставляется 1.0

После этого, если сервер может отдавать клиентам сжатые данные, Вы будете их получать. Обращаю на это внимание, т.к. не всегда удается получить то, что нам нужно. И здесь мы плавно переходим ко второму вопросу.

Как узнать, что сервер вернул сжатые данные?

Ровно также как и попросить отдать данные в сжатом виде – через заголовки. Если сервер возвращает сжатые данные, то в заголовках сервера обязательно есть заголовок Content-Encoding. Для нашего случая он будет таким:

Content-Encoding: gzip

Остается только проанализировать заголовки и определить есть ли в них представленный выше заголовок и его значение. С помощью Synapse это можно сделать, например, так:

with THTTPSend.Create do
  begin
    if HTTPMethod('GET', 'http://webdelphi.ru') then
    begin
      HeadersToList(Headers);//привели заголовки к виду Name=Value
      {проверяем заголовок}
      if Trim(Headers.Values['Content-Encoding']) = 'gzip' then
      begin
        //тут разжимаем данные
      end
      else
        begin
          //так как данные не сжаты - просто считываем строку 
          HTMLMemo.Lines.Text :=ReadStrFromStream(Document,Document.Size)
        end;
    end;
  end;
end;

В представленном выше коде используются только возможности библиотеки Synapse, а именно, процедура HeadersToList и функция ReadStrFromStream находятся в модуле SynaUtil.pas о котором я немного рассказывал в статье «Глубины Synapse или дополнительные возможности библиотеки«.

Теперь можно переходить к основному вопросу статьи.

Как работать с GZip в Synapse?

С подсказки Mifody я открыл для себя довольно простую и одновременно удобную библиотеку для работы с GZip в Delphi под названием delphi zlib. Также как и Synapse эта библиотека содержит набор классов и методов и никаких визуальных или не визуальных компонентов. Чтобы не таскать каждый раз за собой все модули этой библиотеки при создании новых проектов – просто укажите путь к delphi zlib в настройках Delphi:

Tools – Options – Delphi Options – Library – Library Path

zlib_path

Для распаковки сжатых с помощью GZip данных в модуле ZLibExGZ предусмотрены следующие методы:

для строк:

function  GZDecompressStr(const s: RawByteString): AnsiString; overload;
procedure GZDecompressString(var result: UnicodeString; const s: RawByteString); overload;

И ещё несколько переопределенных методов. На мой взгляд, представленные выше два метода наиболее удобны в использовании для использования их совместно с Synapse.

для потоков:

procedure GZDecompressStream(inStream, outStream: TStream); overload;

И ещё несколько переопределенных методов. Соответственно в различных ситуациях нам будет удобно пользоваться либо методами для строк, либо для потоков. Например, использование метода GZDecompressStream может оказаться удобным в таком случае:

var
  DecompStream: TStringStream;
begin
  with THTTPSend.Create do
  begin
    Headers.Add('Accept-Encoding: gzip,deflate');
    Protocol:='1.1';
    if HTTPMethod('GET', 'http://webdelphi.ru') then
    begin
      HeadersToList(Headers);
      if Trim(Headers.Values['Content-Encoding']) = 'gzip' then
      begin
        DecompStream := TStringStream.Create;
        try
          GZDecompressStream(Document, DecompStream);
          Memo1.Lines.Text := DecompStream.DataString;
        finally
          DecompStream.Free
        end;
      end
      else
        Memo1.Lines.Text :=ReadStrFromStream(Document,Document.Size)
    end;
  end;
end;

Можно переписать этот же код, но используя функцию для распаковки строки:

var
  S: RawByteString;
begin
  with THTTPSend.Create do
  begin
    if CheckBox1.Checked then
      Headers.Add('Accept-Encoding: gzip,deflate');
    if HTTPMethod('GET', 'http://webdelphi.ru') then
    begin
      HeadersToList(Headers);
      S:=ReadStrFromStream(Document,Document.Size);
      if Trim(Headers.Values['Content-Encoding']) = 'gzip' then
        Memo1.Lines.Text := GZDecompressStr(S)
      else
        Memo1.Lines.Text:=S;
    end;
  end;

Приведенный выше листинги возвращают один и тот же результат — распакованные данные. Теперь подведем небольшой итог по работе с GZip в Synapse:

  1. Для того, чтобы попробовать получить данные сжатые GZip серверу необходимо передать заголовок ACCEPT-ENCODING
  2. Для гарантированного получения gzip (если сервер поддерживает такую возможность) необходимо свойству Protocol у объекта THTTPSend всегда указывать значение ‘1.1’. Для проверки, попробуйте получить сжатые данные от Яндекс, не указывая версию протокола 1.1.
  3. Для распаковки данных удобно использовать библиотеку delphi zlib в состав которой входят методы для распаковки строк и потоков с данными.
  4. Как отметил в своем сообщении Mifody, при работе с жатыми данными могут возникать непредвиденные ситуации, например, как на mail.ru – ошибка при распаковке данных, несмотря на то, что данные приходят сжатыми GZip. Решение этой проблемы пока не нашел.

Что касается Synapse в принципе, может мне и кажется, но в последнее время в Рунете все чаще и чаще начинают использовать эту библиотеку взамен Indy, что не может не радовать :).

Начиная с Delphi XE8 в стандартную поставку Delphi включили новую библиотеку для работы с HTTP — HTTP Client API. В этой библиотеке, начиная с Delphi 10.3 Rio Release 1 добавлена возможность автоматической распаковки данных, сжатых GZip.

Удачных Вам разработок!

Книжная полка

Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
купить книгу delphi на ЛитРес
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
купить книгу delphi на ЛитРес
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
17 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Mifody
Mifody
28/06/2011 00:23

Уфс, Влад, быстро ты ;). Ну и я первым отмечусь :).
Насчет более частого использования синапса — так приходится ведь, либо бороться с багами инди, либо переходить на синапс. ICS вроде тоже ничего, но я не люблю пользовать VCL для не визуальных действий.
З.Ы. За ссылку спасибо ;), хоть и закрытую :)

Моторокер
Моторокер
28/06/2011 09:56

Всякие Касперские и прочие веб-антивирусы всё равно отключают GZIP сжатие, поэтому его поддержка может иметь разве что теоретическое значение.

Mifody
Mifody
28/06/2011 11:26

Нуууу, «новые горизонты», это как сказать. Ты (да и я то же у себя) в основном рассматривал THTTPSend, а там много чего еще есть. Я вот, кстати, только вчера обнаружил (точнее наконец посмотрел повнимательней) на модули утилит и работу с текстом. Блин, я парился искал как из UTF-8 перевести страницу, а там функция есть оказывается :). Так что еще есть про что писать ;). Кста, ты как то писал про отправку почты, а получение не делал? А то я по IMAP сдернул письмо, не могу въехать как распарсить его. Только заголовок сделал,  а тело пока не получилось :(. Хотя тока… Подробнее »

Mifody
Mifody
28/06/2011 15:12

«разве что теоретическое значение»
Позволю себе не в корне не согласиться. GZIP стоит использовать, когда его использование НЕОБХОДИМО. У себя я писал, что когда надо получить несколько страниц — выигрыш не принципиален. А когда нужны десятки тысяч страниц?
Вообще я честно говорил заказчикам: «хотите быстрый парсер, тогда если он ругается — отключайте нафиг файерволы и антивиры». Синапс по определению не пропустит вирус, ну конечно если бинарники не скачивать ;). А так, разница 18 часов или порядка 4-5 часов работы программы — по моему выигрыш существенный, и практическая стророна прослеживается ;).

Mifody
Mifody
28/06/2011 15:45

Vlad, кстати я кажись разобрался с mail.ru. У себя уже написал :). Если в кратце — конец данных кривой у них, ни контрольная сумма ни указанный объем данных не совпадает с реальностью. Похоже эти «кулибины» сами данные упаковывают, только спецификацию не дочитали.

clHttp
clHttp
20/07/2011 00:13

По ссылке ниже можно скачать модуль для работы synapse с GZip «HTTP with gzip»
http://sourceforge.net/projects/visualsynapse/files/Synapse%20User%20Contributions/HTTP%20with%20gzip/
 
// Visual Synapse: source — http://sourceforge.net/projects/visualsynapse/

tonik
tonik
17/08/2012 02:24

Кто-нибудь сталкивался с проблемой распаковки файлов docx, xlsx, pptx и т.п.?
У меня после использования GZDecompressStream размер распакованного файла превышает размер исходного, и при открытии файла — ошибка.
Файлы других форматов распаковываются нормально.

KSoftware
03/11/2012 23:21

Будет ли работать скачивание файлов?Ну тоесть что бы они тоже были сжаты!

Mifody
Mifody
05/11/2012 12:31

[b]KSoftware[/b] — скорее нет, чем да. На самом деле все это проверить можно изначально в снифере. Если в браузере файлы/данные приходят сжатыми — то можно, если нет, то скорее всего нельзя

Сергей
Сергей
15/05/2013 12:39

У меня в скачиваемом архиве 4 файла: .pdf, .xml, и два .sig
Можно ли содержимое файла xml как то считать сразу из потока, чтоб не извлекать из архива.

Mifody
Mifody
15/05/2013 14:23
Ответить на  Сергей

http://www.base2ti.com/?id=delphi.zlib
это если нет в самой делфе. Я ее вроде то-же использовал в свое время :)

Beast2040
Beast2040
17/05/2013 02:13

Использовал fwzip так как при распаковке с помощью zlib (по ссылке мифодия)вываливалась ошибка, подозреваю что из за delphi7.

Read[0].ExtractToStream(Data, ''); //var Read: TFWZipReader;
Str:=Utf8ToAnsi(Data.DataString); //var Data: TStringStream;

Юрий
Юрий
28/10/2014 11:05

Спасибо за GZip очень выручил!