Sample code for 30+ languages & platforms
Delphi ActiveX

HTTP Download in Parallel with Simultaneous Range Requests

See more HTTP Examples

Demonstrates how to download a large file with parallel simultaneous requests, where each request downloads a segment (range) of the remote file.

Chilkat Delphi ActiveX Downloads

Delphi ActiveX
uses
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Chilkat_TLB;

...

procedure TForm1.Button1Click(Sender: TObject);
var
success: Integer;
http: TChilkatHttp;
url: WideString;
resp: TChilkatHttpResponse;
remoteFileSize: Integer;
chunkSize: Integer;
http1: TChilkatHttp;
http2: TChilkatHttp;
http3: TChilkatHttp;
http4: TChilkatHttp;
sbRange: TChilkatStringBuilder;
numReplaced: Integer;
task1: IChilkatTask;
task2: IChilkatTask;
task3: IChilkatTask;
task4: IChilkatTask;
numLive: Integer;
numErrors: Integer;
fac: TCkFileAccess;
bSame: Integer;

begin
success := 0;

// This requires the Chilkat API to have been previously unlocked.
// See Global Unlock Sample for sample code.

http := TChilkatHttp.Create(Self);

// First get the size of the file to be downloaded.
url := 'https://www.chilkatsoft.com/hamlet.xml';

resp := TChilkatHttpResponse.Create(Self);
success := http.HttpNoBody('HEAD',url,resp.ControlInterface);
if (success = 0) then
  begin
    Memo1.Lines.Add(http.LastErrorText);
    Exit;
  end;

remoteFileSize := resp.ContentLength;

Memo1.Lines.Add('Downloading ' + IntToStr(remoteFileSize) + ' bytes...');

// Let's download in 4 chunks.
// (the last chunk will be whatever remains after the 1st 3 equal sized chunks)
chunkSize := remoteFileSize / 4;

// The Range header is used to download a range from a resource
// Range: bytes=<range-start>-<range-end>
// or
// Range: bytes=<range-start>-

// We're writing code this way for clarity..
http1 := TChilkatHttp.Create(Self);
http2 := TChilkatHttp.Create(Self);
http3 := TChilkatHttp.Create(Self);
http4 := TChilkatHttp.Create(Self);

sbRange := TChilkatStringBuilder.Create(Self);
sbRange.SetString('bytes=<range-start>-<range-end>');
numReplaced := sbRange.ReplaceI('<range-start>',0);
numReplaced := sbRange.ReplaceI('<range-end>',chunkSize - 1);
Memo1.Lines.Add(sbRange.GetAsString());
http1.SetRequestHeader('Range',sbRange.GetAsString());

sbRange.SetString('bytes=<range-start>-<range-end>');
numReplaced := sbRange.ReplaceI('<range-start>',chunkSize);
numReplaced := sbRange.ReplaceI('<range-end>',2 * chunkSize - 1);
Memo1.Lines.Add(sbRange.GetAsString());
http2.SetRequestHeader('Range',sbRange.GetAsString());

sbRange.SetString('bytes=<range-start>-<range-end>');
numReplaced := sbRange.ReplaceI('<range-start>',2 * chunkSize);
numReplaced := sbRange.ReplaceI('<range-end>',3 * chunkSize - 1);
Memo1.Lines.Add(sbRange.GetAsString());
http3.SetRequestHeader('Range',sbRange.GetAsString());

sbRange.SetString('bytes=<range-start>-');
numReplaced := sbRange.ReplaceI('<range-start>',3 * chunkSize);
Memo1.Lines.Add(sbRange.GetAsString());
http4.SetRequestHeader('Range',sbRange.GetAsString());

// Start each range download
task1 := http1.DownloadAsync(url,'qa_output/chunk1.dat');
task1.Run();

task2 := http2.DownloadAsync(url,'qa_output/chunk2.dat');
task2.Run();

task3 := http3.DownloadAsync(url,'qa_output/chunk3.dat');
task3.Run();

task4 := http4.DownloadAsync(url,'qa_output/chunk4.dat');
task4.Run();

// Wait for the downloads to complete.
numLive := 4;
while numLive > 0 do
  begin
    numLive := 0;
    if (task1.Live = 1) then
      begin
        numLive := numLive + 1;
      end;
    if (task2.Live = 1) then
      begin
        numLive := numLive + 1;
      end;
    if (task3.Live = 1) then
      begin
        numLive := numLive + 1;
      end;
    if (task4.Live = 1) then
      begin
        numLive := numLive + 1;
      end;
    if (numLive > 0) then
      begin
        // SleepMs is a convenience method to cause the caller to sleep for N millisec.
        // It does not cause the given task to sleep..
        task1.SleepMs(10);
      end;
  end;

// All should be downloaded now..
// Examine the result of each Download.
numErrors := 0;
if (task1.GetResultBool() = 0) then
  begin
    Memo1.Lines.Add(task1.ResultErrorText);
    numErrors := numErrors + 1;
  end;
if (task2.GetResultBool() = 0) then
  begin
    Memo1.Lines.Add(task2.ResultErrorText);
    numErrors := numErrors + 1;
  end;
if (task3.GetResultBool() = 0) then
  begin
    Memo1.Lines.Add(task3.ResultErrorText);
    numErrors := numErrors + 1;
  end;
if (task4.GetResultBool() = 0) then
  begin
    Memo1.Lines.Add(task4.ResultErrorText);
    numErrors := numErrors + 1;
  end;
if (numErrors > 0) then
  begin

    Exit;
  end;

// All downloads were successful.
// Compose the file from the parts.
fac := TCkFileAccess.Create(Self);
success := fac.ReassembleFile('qa_output','chunk','dat','qa_output/hamlet.xml');
if (success = 0) then
  begin
    Memo1.Lines.Add(fac.LastErrorText);
  end
else
  begin
    Memo1.Lines.Add('Success.');
  end;

// Let's download in the regular way, and then compare files..
success := http.Download(url,'qa_output/hamletRegular.xml');

// Compare files.
bSame := fac.FileContentsEqual('qa_output/hamlet.xml','qa_output/hamletRegular.xml');
Memo1.Lines.Add('bSame = ' + IntToStr(Ord(bSame)));
end;