Викачуємо файли з інтернету - статті

Викачуємо файли з інтернету

Завдання: завантажити файл http в зазначену папку з використанням потоку.

Кидаємо на форму два TEdit, TProgressBar, одну кнопку і TSaveDialog.

Викачуємо файли з інтернету - статті

Для кнопки пишемо маленький обробник:

Тепер на форму додамо IdHTTP і кнопку (Button2) з написом "почати закачування".

З оброблювачем поки почекаємо, а напишемо найскладніше - клас для потоку.

Перші два рядки зроблені для того, щоб було видно, де вписати код. І натискаємо Ctrl + Shift + C. Delphi допише трохи коду. Він тепер буде виглядати так:

type
TDownLoader = class (TThread)
private
FToFolder: string;
FURL: string;
protected
procedure Execute; override;
published
public
property URL: string read FURL write FURL;
property ToFolder: string read FToFolder write FToFolder;
end;

Компонент idHTTP був кинутий на форму тільки з однією метою - щоб Delphi додала все заголовки в uses. Потім його можна буде видалити. Але можна і самостійно вписати в uses файл idHTTP.

Головний код потоку

Отже, код обробника:

procedure TDownLoader.Execute;
var
http: TIdHTTP;
str: TFileStream;
begin
// створю клас для закачування
http: = TIdHTTP.Create (nil);
// каталог, куди файл покласти
ForceDirectories (ExtractFileDir (ToFolder));
// Потік для збереження
str: = TFileStream.Create (ToFolder, fmCreate);
try
// Качаем
http.Get (url, str);
finally
// Нас вчили чистити за собою
http.Free;
str.Free;
end;

Пам'ятайте, що в цій процедурі не можна безпосередньо звертатися до компонентів форми і іншим потокам. Це робиться спеціальним чином, який називається синхронізацією.

І нарешті, обробник для кнопки:

procedure TForm1.Button2Click (Sender: TObject);
var d: TDownLoader;
begin
// Створимо клас потоку.
// Потік для початку буде зупинений
d: = TDownLoader.Create (true);
// Передамо параметри потоку
d.URL: = Edit1.Text;
d.ToFolder: = Edit2.Text;
// Потік повинен видалити себе по завершенню своєї роботи
d.FreeOnTerminate: = true;
// І запустимо його на закачку.
d.Resume;
// Тепер з процедури ми вийдемо, але потік працює
// і живе своїм життів
end;

Тепер наш код вже може качати, але у нас немає прогресу, немає повідомлення про завершення закачування.

Додамо для початку повідомлення про закачування. Додамо в public частина форми додамо рядок:

і натиснемо Ctrl + Shift + C.

З'явиться новий обробник, який ми доповнимо одним рядком. Я виводжу просто повідомлення про готовність. Пам'ятайте, ця процедура буде викликатися, коли потік виконає всю свою роботу (завершиться процедура Execute). Тільки в цій процедурі можна одночасно звертатися до компонентів форми і даними потоку.

І додамо її виклик в обробнику кнопки запуску:

// Потік повинен видалити себе по завершенню своєї роботи
d.FreeOnTerminate: = true;
d.OnTerminate: = thrTerminate;

Тепер у нас залишилася ще одна проблема - оживити прогрес-бар. Але тут нас підстерігає один момент: з потоку не можна просто так звертатися до компонентів форми. Точніше, правило звучить так: з одного потоку не можна звертатися до даних іншого потоку (змінним, коду) без спеціальних методів. Таких методів є два. Перший - ми зупиняємо один потік, а другий на цей час отримує доступ до його змінним. Або другий потік просить перший виконати якийсь код, а сам на цей час засинає.

Спосіб два, яким ми і скористаємося - це посередник. Ми шолом повідомлення посереднику, щоб він повідомив іншому потоку (а може і групі), щоб він щось зробив. Цей спосіб хороший тим, що потік може обробляти повідомлення не з примусу, а за можливості. Тобто повідомлення стають в чергу. В якості посередника ми виберемо саме середовище Windows.

Отже, Windows при посилці повідомлення дозволяє передавати два цілочисельних параметра. Перший ми будемо використовувати як ідентифікатор дій, другий - як додатковий параметр.

Поїхали далі. Після uses перед Type вставимо рядок:

const
MY_MESS = WM_USER + 100;

procedure MyProgress (var msg: TMessage); message MY_MESS;

І заповітне Ctrl + Shift + C

У новоствореного обработчике пишемо таке:

Те-є, якщо передали тип операції 0 - значить потрібно форматувати прогрес. Передали 1 - потрібно виставити позицію. Як саме - зазначено в іншому параметрі (msg.LParam).

А тепер повертаємося до наших обробникам IdHTTP, які ми залишили без реалізації. Туди потрібно вписати всього по рядку. Нижче приведена реалізація.

procedure TDownLoader.IdHTTP1Work (ASender: TObject; AWorkMode: TWorkMode;
AWorkCount: Integer);
begin
PostMessage (Application.MainForm.Handle, MY_MESS, 1, AWorkCount);
end;

procedure TDownLoader.IdHTTP1WorkBegin (ASender: TObject; AWorkMode: TWorkMode;
AWorkCountMax: Integer);
begin
PostMessage (Application.MainForm.Handle, MY_MESS, 0, AWorkCountMax);
end;

Природно, є ще кілька способів все синхронізувати. Але такий спосіб мені здається дуже простим і надійним, тому що не буде блокувань, повідомлення будуть оброблятися в міру можливості (у міру сил :-)).

Для вставки посилання на цю статтю на іншому сайті використовуйте наступний HTML-код:

ДОПОМОЖІТЬ, А ЯК РЕАЛІЗУВАТИ ЩОБ ПІСЛЯ ЗАВЕРШЕННЯ ПОТОКА виконували свій КОД ДЛЯ КОЖНОГО скачав файлу. А НЕ ЗАГАЛОМ, ЯК ЗРОБЛЕНО У СТАТТІ (procedure TForm1.thrTerminate (Sender: TObject);
begin
ShowMessage ( 'Готово');
end;