HTTP Client API — это относительно новый фреймворк, появившейся впервые в Delphi XE8 и, получивший развитие в более поздних версиях Delphi. HTTP Client API позволяет вашим приложениям Delphi отправлять запросы к серверам по протоколу HTTP и работать с ответами. В состав фреймворка входят два компонента: TNetHTTPClient и TNetHTTPRequest, а также классы и интерфейсы для работы по HTTP-протоколу.
Этот пример не использует компоненты, однако использует класс THTTPClient для скачивания файла из Сети с использованием HTTP Client API.
Основная информация по компоненту | ||
Исходник официального примера и документация | DocWiki |
Это приложение использует HTTP Client API для загрузки файла. Загружаемый файл разделяется на 4 части и приложение использует несколько потоков для одновременной загрузки частей.
Приложение использует следующие элементы управления:
- EditFileName: позволяет указать имя файла.
- EditURL: позволяет указать URL-адрес файла.
- BStartDownload: запускает обработчик событий BStartDownloadClick.
- LabelGlobalSpeed: отображает среднюю скорость загрузки.
- ImageList1: содержит изображения кнопки «стоп».
- Button1, Button2, Button3, Button4: запуск обработчика событий ButtonCancelClick.
- ProgressBarPart1, ProgressBarPart2, ProgressBarPart3, ProgressBarPart4: отображение хода загрузки соответствующей части файла.
- Label1, Label2, Label3, Label4: отображение скорости загрузки соответствующей части файла.
- Memo1: отображает сообщения из приложения для пользователя.
Внешний вид демонстрационного приложения представлен на рисунке ниже:
Демонстрационный пример
Клик по кнопке «Start Download» запускает анонимный поток внутри которого работает единственный метод:
procedure TFormDownloadDemo.BStartDownloadClick(Sender: TObject); begin ... TThread.CreateAnonymousThread(procedure begin SampleDownload; end).Start; end;
Процедура SampleDownload выглядит следующим образом:
procedure TFormDownloadDemo.SampleDownload; var LClient: THTTPClient; URL: string; LResponse: IHTTPResponse; StFile: TFileStream; LFileName: string; LStart, LEnd, LSize, LFragSize: Int64; I: Integer; LDownloadThreads: array of TDownloadThread; LFinished: Boolean; LStartTime, LEndTime: Cardinal; begin LClient := THTTPClient.Create; LFileName := TPath.Combine(TPath.GetDocumentsPath, EditFileName.Text); TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('File location = ' + LFileName); Memo1.Lines.Add('Downloading ' + URL + ' ...'); Application.ProcessMessages; end); try URL := EditURL.Text; //Проверяем поддерживает ли сервер возобновление загрузки if LClient.CheckDownloadResume(URL) then begin //запрашиваем с сервера заголовки (Headers). //В заголовках должен вернуться размер загружаемого файла LResponse := LClient.Head(URL); //Читаем размер файла, который необходимо скачать LSize := LResponse.ContentLength; //выделяем место под файл StFile := TFileStream.Create(LFileName, fmCreate); try STFile.Size := LSize; finally STFile.Free; end; // Делим весь размер файла на 4 части //NumThreads = 4 LFragSize := LSize div NumThreads; //указываем с какого места в потоке вести запись данных и до какого места LStart := 0; LEnd := LStart + LFragSize; //создаем массив потоков //каждый поток будет качать свою часть файла SetLength(LDownloadThreads, NumThreads); for I := 0 to NumThreads - 1 do begin if FClosingForm then Break; // Создаем новый поток LDownloadThreads[I] := TDownloadThread.Create(URL, LFileName, I, LStart, LEnd); LDownloadThreads[I].OnThreadData := ReceiveThreadDataEvent; TThread.Synchronize(nil, procedure begin // Настраиваем максимальное значение у ProgressBar'a if LEnd >= LSize then begin ProgressBarArray[I].Max := LFragSize - (LEnd - LSize); LEnd := LSize; end else ProgressBarArray[I].Max := LFragSize; //обнуляем значения ProgressBar'а ProgressBarArray[I].Min := 0; ProgressBarArray[I].Value := 0; ButtonCancelArray[I].Enabled := True; LabelProgressArray[I].Text := '0 KB/s'; end); // Обновляем значения LStart и LEnd для следующего потока LStart := LStart + LFragSize; LEnd := LStart + LFragSize; end; // Начинаем загрузку частей файла LStartTime := TThread.GetTickCount; for I := 0 to NumThreads - 1 do LDownloadThreads[I].Start; // Ждем пока потоки не отработают LFinished := False; while not LFinished and not FClosingForm do begin LFinished := True; for I := 0 to NumThreads - 1 do LFinished := LFinished and LDownloadThreads[I].Finished; end; //показываем пользователю среднюю скорость загрузки LEndTime := TThread.GetTickCount - LStartTime; TThread.Synchronize(nil, procedure begin LabelGlobalSpeed.Text := Format('Global Speed: %d KB/s', [((LSize*1000) div LEndTime) div 1024]); end); // Очищаем потоки for I := 0 to NumThreads - 1 do LDownloadThreads[I].Free; end else begin //сервер не поддерживает возобновление загрузки TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('Server has NOT resume download feature'); end); end; finally LClient.Free; TThread.Synchronize(nil, procedure begin BStartDownload.Enabled := True; end); FAllowFormClose := True; end; end;
Таким образом, SampleDownload инициирует загрузку файла по частям, в то время, как процесс «общения» с сервером осуществляется в отдельных потоках TDownloadThread.
TDownloadThread имеет следующее описание:
type TDownloadThreadDataEvent = procedure(const Sender: TObject; ThreadNo, ASpeed: Integer; AContentLength: Int64; AReadCount: Int64; var Abort: Boolean) of object; TDownloadThread = class(TThread) private FOnThreadData: TDownloadThreadDataEvent; protected FURL, FFileName: string; FStartPoint, FEndPoint: Int64; FThreadNo: Integer; FTimeStart: Cardinal; procedure ReceiveDataEvent(const Sender: TObject; AContentLength: Int64; AReadCount: Int64; var Abort: Boolean); public constructor Create(const URL, FileName: string; ThreadNo: Integer; StartPoint, EndPoint: Int64); destructor Destroy; override; procedure Execute; override; property OnThreadData: TDownloadThreadDataEvent write FOnThreadData; end;
Метод Execute потока выглядит следующим образом:
procedure TDownloadThread.Execute; var LResponse: IHTTPResponse; LStream: TFileStream; LHttpClient: THTTPClient; begin inherited; //создаем объект клиента HTTP LHttpClient := THTTPClient.Create; try //настраиваем обработчик события OnReceiveData //событие срабатывает при получении очередного ответа от сервера LHttpClient.OnReceiveData := ReceiveDataEvent; //настраиваем поток для хранения данных LStream := TFileStream.Create(FFileName, fmOpenWrite or fmShareDenyNone); try FTimeStart := GetTickCount; LStream.Seek(FStartPoint, TSeekOrigin.soBeginning); //отправляем запрос на получение части файла LResponse := LHttpClient.GetRange(FURL, FStartPoint, FEndPoint, LStream); finally LStream.Free; end; finally LHttpClient.Free; end; end;
Обработчик OnReceiveData выглядит следующим образом:
procedure TDownloadThread.ReceiveDataEvent(const Sender: TObject; AContentLength, AReadCount: Int64; var Abort: Boolean); var LTime: Cardinal; LSpeed: Integer; begin if Assigned(FOnThreadData) then begin LTime := GetTickCount - FTimeStart; if AReadCount = 0 then LSpeed := 0 else LSpeed := (AReadCount * 1000) div LTime; //отправляем событие потока OnThreadData //передаем номер потока, скорость загрузки, размер скачанных данных FOnThreadData(Sender, FThreadNo, LSpeed, AContentLength, AReadCount, Abort); end; end;
Этот демонстрационный пример показывает как загрузить файл с помощью HTTP Client API и при этом использует синхронный режим. В этом случае, для того, чтобы интерфейс приложения не «подвисал» используются отдельные потоки для работы с каждой частью файла. Стоит отметить, что HTTP Client API поддерживает также и асинхронный режим работы.
На рисунке ниже представлен пример работающего демонстрационного приложения:
Смотрите также
- HTTP Client API в Delphi
- Как в Delphi скачать файл с использованием HTTP Client API?
- Как в Delphi скачать файл в асинхронном режиме с использованием HTTP Client API?
- Работа с GZip в HTTP Client API (Delphi 10.3 Rio)
При подготовке статьи использовалась информация со следующих ресурсов:
- Официальный репозиторий демонстрационных примеров Delphi на SourceForge
- Информация по работе с классом THTTPClient на сайте Embarcadero