Главная » Статьи » Программирование в Delphi

Скачиваем файлы из интернета
Задача: скачать файл по 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.

Категория: Программирование в Delphi | Добавил: Nikol05 (06.08.2009)
Просмотров: 1934 | Рейтинг: 4.0/4
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]