Вы здесь

Synapse. Скачиваем данные с ProgressBar’ом

Если Вы используете в работе с Сетью библиотеку Synapse, то, вероятно задумывались о том как сделать так, чтобы не просто получать или отправлять данные, но и видеть весь ход процесса. Например, чтобы при скачивании данных заполнялся ProgressBar, или чтобы в процессе работы THTTPSend видеть весь лог его работы, начиная от создания и, заканчивая закрытием сокета.
Сделать это достаточно просто, подключив в uses всего один модуль – blcksock.

У объекта THTTPSend имеются следующие свойства:

property DownloadSize: integer read FDownloadSize;
property UploadSize: integer read FUploadSize;

Которые, судя по описанию, в каждый момент времени содержат размер полученных и отправленных данных в байтах. И для того, чтобы получать эти значения достаточно определить событие OnStatus у сокета. На самом деле эти свойства содержат либо 0 и это значит, что данные ещё не принимаются и не отправляются, либо уже принятый или отправленный размер данных.
Убедиться в этом можно следующим образом. Создадим новое приложение и подключим в Uses модули httpsend и blcksock. На форму положим Memo в который будем выводить значения DownloadSize.
Создадим глобальную переменную HTTP: THTTPSend и напишем обработчик события OnStatus у сокета:

procedure TForm4.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
begin
  Memo1.Lines.Add(IntToStr(HTTP.DownloadSize));
end;

А на OnCreate главной формы:

  HTTP:=THTTPSend.Create;
  HTTP.Sock.OnStatus:=Status;
  HTTP.HTTPMethod('GET','http://www.kraeg.ru');

Теперь запустим программу и посмотрим, что выпишется в Memo. У меня получились следующие строки:

0
0
[....]
0
0
65566
65566
[....]
65566
65566
65566
65566
65566

То есть – прогресса толком и нет. Либо 0, либо сразу всё. Поэтому использование этих свойств оставим на потом. А лучше воспользуемся теми данными, которые возвращает нам сокет, т.е.

Reason: THookSocketReason;
const Value: String;

Reason – это тип события
Value - значение, возвращаемое при определенном событии.
Всего на OnStatus можно получить данные по четырнадцати различным событиям, но для нас сейчас особую роль будут играть два из них: HR_ReadCount и HR_WriteCount. При первом событии в Value вернется строка, содержащая количество прочитанных байтов, при втором – отправленных.
Соответственно, можно завести какую-нибудь переменную типа integer и каждый раз при событии HR_ReadCount или HR_WriteCount прибавлять к этой переменной значение StrToInt(Value) и заполнять ProgressBar.
Осталось только определить максимальное значение у ProgressBar’a (кстати, можете его уже уложить на форму). Сделать это достаточно просто, отправив запрос “HEAD” на нужный адрес и проанализировав заголовок “Content-Length”. Можно воспользоваться, к примеру, такой функцией:

function TForm4.GetLength(const URL: string): integer;
var i:integer;
    size:string;
    ch:char;
begin
  with THTTPSend.Create do
  if HTTPMethod('HEAD',URL) then
    begin
      for I := 0 to Headers.Count - 1 do
        begin
          if pos('content-length',lowercase(Headers[i]))>0 then
            begin
              size:='';
              for ch in Headers[i]do
                if ch in ['0'..'9'] then
                   size:=size+ch;
              Result:=StrToInt(size)+Length(Headers.Text);
              break;
            end;
        end;
    end
  else
    Result:=-1;
end;

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

 Result:=StrToInt(size)+Length(Headers.Text);

Ну, а теперь нам осталось только немного подправить процедуру Status. Создайте ещё одну переменную, например:

Download: integer;

В ней будем хранить общее количество полученных данных. Будем в Memo выписывать весь лог и заполнять ProgressBar, поэтому подключите дополнительно в uses TypInfo и дописываем Status:

if Reason=HR_ReadCount then
    begin
      Download:=Download+StrToInt(Value);
      if ProgressBar1.Max>0 then
        begin
          ProgressBar1.Position:=Download;
          Memo1.Lines.Add('Получено '+Value+' байт');
          Memo1.Lines.Add('Скачано '+IntToStr(Download)+' из '+IntToStr(ProgressBar1.Max));
        end;
   end
  else
    if not (Reason=HR_CanRead) then
      Memo1.Lines.Add(GetEnumName(TypeInfo(THookSocketReason),ord(Reason))+' '+Value);
end;

Теперь вешаем на форму кнопку и в обработчике onClick пишем:

  Memo1.Clear;
  Download:=0;
  ProgressBar1.Max:=GetLength(Edit1.Text);
  ProgressBar1.Position:=0;
  HTTP.HTTPMethod('GET',Edit1.Text);
  Memo1.Lines.Add('Длина заголовков '+IntToStr(Length(HTTP.Headers.Text)));
  HTTP.Clear;

Здесь следует заметить, что после того как данные получены и переданы дальше на обработку обязательно необходимо выполнять Clear у THTTPSend, иначе при следующем GET-запросе объект попробует не только получить данные, но и отправить все, что было получено при предыдущем запросе, включая куки и заголовки, что приведет к ошибке.
Вот теперь можете запускать программу и смотреть как заполняется ProgressBar при скачивании файла, а в Memo выводится лог работы объекта THTTPSend.
Также, для получения каких либо данных от сокета вы можете определить событие OnHeartBeat (“Сердцебиение”), которое будет срабатывать в заданные промежутки времени, но, как говорят сами разработчики Synaps’а – слишком частое использование этого события может привести к “подвисанию” приложения, поэтому лучше сильно не рисковать.

Скачать пример можно с сайта автора статьи.