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

Если Вы используете в работе с Сетью библиотеку 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://webdelphi.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’а — слишком частое использование этого события может привести к «подвисанию» приложения, поэтому лучше сильно не рисковать.

0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
24 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Владимир
Владимир
09/01/2013 21:43

Это понятно, но в том-то и дело, что даже по окончании запроса DownloadSize выдает ноль (это только в случае txt/html.

> Что касается Content-Length, то сервер может, но не обязан выдавать этот заголовок
Но Ваш пример и построен на считывании Content-Length (определение максимального значения ProgressBar-а). Получается не всегда он будет корректно работать?

Ivan_
Ivan_
02/07/2010 16:24

Статья полезная, но есть одно НО!

Что за чудесная конструкция?

[code]
for ch in Headers[i]do
if ch in ['0'..'9'] then size:=size+ch;
Result:=StrToInt(size)+Length(Headers.Text);
break;
end;
[/code]

У меня, например на этом месте
[code]for ch in Headers[i]do [/code]
выдаёт ошибку.

Ivan_
Ivan_
02/07/2010 17:39

Delphi 7.

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

Ivan_
Ivan_
02/07/2010 18:33

В процедуре Status (у меня она по-другому называется) я проверяю вместо Reason=HR_ReadCount — Reason=HR_WriteCount (что скорее всего нужно для того, чтобы посчитать отданное в сеть) и там суммирую.

Вопрос в том, что у меня файл допустим 86520 байт, а через сеть по процедуре Status считается только 21351 (оно всегда одно и тоже отдаёт).

Может быть это только заголовок или что-то по смыслу статическое, но не сам файл.

ЗЫ в комментариях теги [code][/code] не работают).

Ivan_
Ivan_
20/07/2010 02:27

кстати, для тех использует делфи 6-7 часть кода с

for ch in Headers[i]do
if ch in [‘0’..’9′] then size:=size+ch;

запросто переделывается на

for j:=0 to length(Headers.Strings[i]) do begin
ch := Headers.Strings[i][j];
if ch in [‘0’..’9′] then size:=size+ch;
end;

Ivan_
Ivan_
20/07/2010 18:24

Порешал проблему с аплоудом. Всё отлично работает. Осталось только только с МИМЕ типом исходящим разобратсья (чтобы оплучить размер данных, которые надо отослать).

А проблема кроилась собственно говоря в типах данных — я использовал всего лишь дворд (0…65535), а пакеты посылались в 63536 байт, поэтому инкрементилось всё в ноль). А сейчас всё отлично!

Ivan_
Ivan_
22/07/2010 02:09

Про Майм — я немного ошибся. Мне необходимо получить инфу (типо хедера), которую клиент отправляет на сервер перед передачей данных на сервер. Я смог такое реализовать через событие OnMonitor у сокета (вроде). Оттуда по типам событий достал контент-длину (она мне нужна, для реализации аплоуда). И вот так получаю нужные размеры. А потом через то же события OnStatus у сокета смотрю данные. При необходимости могу написать статью или просто поделится решением (в соответсвующем к пониманию виде).

Алексей
Алексей
25/01/2011 18:47

При загрузке страниц не в курсе, а вот при загрузке файла можно легко сделать прогресбар. Вот таким методом.
procedure TFmain.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
begin
PB1.Position := HTTP1.Document.Size;
PB1.Max := HTTP1.DownloadSize;
end;

Владимир
Владимир
09/01/2013 13:04

0
HR_WriteCount 120
0
0
Почему-то скачивается не вся страница.


Получено 8192 байт
Скачано 8192 из 4654264
0
Получено 4332 байт
Скачано 12524 из 4654264
0
0
HR_Error 10054,Connection reset by peer
0
HR_SocketClose
Длина заголовков 455

Если скачиваю exe-файл или картинку, то все нормально, а с html почему-то вот такая фигня.^

Владимир
Владимир
09/01/2013 13:21

Все дело в том, что при скачивании text/html заголовка content-length нет.

Владимир
Владимир
09/01/2013 13:37

Также HTTP.DownloadSize при скачивании html-страницы всегда выдает 0. (хотя вся страница на самом деле скачивается).
При скачивании jpg или exe-файла HTTP.DownloadSize показывает сначала 0, а потом значения, равные content-length.
Значит ли это, что в DownloadSize значение записывается прямо из заголовка content-length? (раз при html нет content-length, и DownloadSize тоже нуль).

Макаров Василий
Макаров Василий
05/11/2013 16:07

Уважаемый, а почему у вас все циклы сделаны в манере for in?
Не все же пользуются последними Delphi, сделайте так чтобы более ранние версии воспринимали исходный код без проблем.

Артём
Артём
13/11/2016 19:02

Скажите пожалуйста, как синапсом можно получить файл без сохранения куда либо (в память или на диск)? Есть большой файл на сервере, нужно просто гнать траффик с него без дальнейшей обработки.

Александр Дмитриев

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