Скачиваем файлы из интернета
Автор: Вадим К
Задача: скачать файл по http в указанную папку с использованием потока.
Делаем форму
Бросаем на форму два TEdit, TProgressBar, одну кнопку и TSaveDialog.
Для кнопки пишем маленький обработчик:
//Этой строкой мы скопируем имя файла SaveDialog1.FileName:=copy(Edit1.Text,LastDelimiter('\⁄',Edit1.Text)+1,maxint);
if SaveDialog1.Execute then
Edit2.Text:=SaveDialog1.FileName;
Теперь на форму добавим IdHTTP и кнопку (Button2) с надписью "начать закачку".
Делаем поток
С обработчиком пока повременим, а напишем самое сложное – класс для потока.
{$R *.dfm}
//---------------------------------------
type
TDownLoader = class(TThread)
protected
procedure Execute; override;
public
property URL:string read FURL write FURL;
property ToFolder:string read FToFolder write FToFolder;
end;
Первые две строки сделаны для того, чтобы было видно, где вписать код. И нажимаем 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;
procedure TForm1.Button1Click(Sender: TObject);
begin
SaveDialog1.FileName:=copy(Edit1.Text,LastDelimiter('\⁄',Edit1.Text)+1,maxint);
if SaveDialog1.Execute then
Edit2.Text:=SaveDialog1.FileName;
end;
{ TDownLoader }
procedure TDownLoader.Execute;
begin
//здесь мы начнём писать код работы
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;
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 часть формы добавим строку:
public
{ public declarations }
procedure thrTerminate(Sender:TObject);
end;
и нажмём Ctrl+Shift+C.
Появится новый обработчик, который мы дополним одной строкой. Я вывожу просто сообщение о готовности. Помните, эта процедура будет вызваться, когда поток выполнит всю свою работу (завершится процедура Execute). Только в этой процедуре можно одновременно обращаться к компонентам формы и данным потока.
procedure TForm1.thrTerminate(Sender: TObject);
begin
ShowMessage('Готово');
end;
И добавим её вызов в обработчике кнопки запуска:
//Поток должен удалить себя по завершению своей работы
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
В свежесозданном обработчике пишем такое:
procedure TForm1.MyProgress(var msg: TMessage);
begin
case msg.WParam of
0: begin ProgressBar1.Max:=msg.LParam;ProgressBar1.Position:=0; end;
1: ProgressBar1.Position:=msg.LParam;
end;
end;
То-есть, если передали тип операции 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;
Заключение
Естественно, есть ещё несколько способов всё синхронизировать. Но такой способ мне кажется очень простым и надёжным, так как не будет блокировок, сообщения будут обрабатываться по мере возможности (по мере сил :-)).
В сопутствующем архиве вы найдёте полный рабочий пример. Он будет компилироваться на Delphi 2006 и Indy 10. На 7 Delphi сходу может не скомпилироваться, но если вы будете делать вручную и немного думать, то всё заработает. Причина – с Delphi 7 поставляется более старая версияIndy.
Автор: Вадим К
Статья добавлена: 7 августа 2007
Следующая статья: О костылях »
Зарегистрируйтесь/авторизируйтесь,
чтобы оценивать статьи.
Для вставки ссылки на данную статью на другом сайте используйте следующий HTML-код:
Ссылка для форумов (BBCode):
Быстрая вставка ссылки на статью в сообщениях на сайте:
{{a:39}} (буква a — латинская) — только адрес статьи (URL);
{{статья:39}} — полноценная HTML-ссылка на статью (текст ссылки — название статьи).
Поделитесь ссылкой в социальных сетях:
Комментарии читателей к данной статье
Репутация: нет |
Golos (25 февраля 2016, 15:01): ПОМОГИТЕ, А КАК РЕАЛИЗОВАТЬ ЧТОБЫ ПОСЛЕ ЗАВЕРШЕНИЯ ПОТОКА ВЫПОЛНЯЛСЯ СВОЙ КОД ДЛЯ КАЖДОГО СКАЧЕННОГО ФАЙЛА , А НЕ В ОБЩЕМ, КАК СДЕЛАНО В СТАТЬЕ (procedure TForm1.thrTerminate(Sender: TObject);
begin ShowMessage('Готово'); end; |
Репутация: +66 |
DNK (22 февраля 2016, 20:34): А с моей точки зрения это как раз частность.
|
Репутация: нет |
Golos (22 февраля 2016, 14:47): ПОМОГИТЕ, А КАК РЕАЛИЗОВАТЬ ЧТОБЫ ПОСЛЕ ЗАВЕРШЕНИЯ ПОТОКА ВЫПОЛНЯЛСЯ СВОЙ КОД ДЛЯ КАЖДОГО СКАЧЕННОГО ФАЙЛА , А НЕ В ОБЩЕМ, КАК СДЕЛАНО В СТАТЬЕ (procedure TForm1.thrTerminate(Sender: TObject);
begin ShowMessage('Готово'); end; |
Репутация: нет |
Vogel (10 августа 2012, 17:24): Зарегистрировался специально ради этой статьи. Способ полной синхронизации с закачкой просто отличный. Очень пригодился. До этого пользовался IDHTTP гетом без всяких посторонних классов, не опытный пока. Автору огромное спасибо, и просто все и работает все как швейцарские часы. А прогресс бар может не работать еще потому, что автор в процедуре TDownLoader.Execute не связал события onwork и onwork begin переменной http с событиями IDHTTP1, хотя в прикрепленном исходнике все это присутствует.
|
Репутация: нет |
astalavista (5 июня 2010, 18:40): почемуто прогресс мёртв((
|
Репутация: +359 |
Вадим К (2 ноября 2008, 01:17): Не будет работать, если версия Indy старая.
Либо надо обновить до последней, либо использовать 2006 делфи и старше (там уже нужная версия инди), либо подумать немного и исправить параметры на правильные. |
Оставлять комментарии к статьям могут только зарегистрированные пользователи.