Sample code for 30+ languages & platforms
Pascal (Lazarus/Delphi)

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 Pascal (Lazarus/Delphi) Downloads

Pascal (Lazarus/Delphi)
program ChilkatDemo;

// Demonstrates using the Chilkat Pascal wrapper via the C bridge DLL.
// Builds as a console application under Lazarus (FPC) or Delphi.

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}
{$APPTYPE CONSOLE}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  SysUtils,
  CkDllLoader,
  Chilkat.Http,
  Chilkat.StringBuilder,
  Chilkat.Task,
  Chilkat.HttpResponse,
  Chilkat.FileAccess;

// ---------------------------------------------------------------------------

procedure RunDemo;
var
  success: Boolean;
  http: THttp;
  url: string;
  resp: THttpResponse;
  remoteFileSize: Integer;
  chunkSize: Integer;
  http1: THttp;
  http2: THttp;
  http3: THttp;
  http4: THttp;
  sbRange: TStringBuilder;
  numReplaced: Integer;
  task1: TTask;
  task2: TTask;
  task3: TTask;
  task4: TTask;
  numLive: Integer;
  numErrors: Integer;
  fac: TFileAccess;
  bSame: Boolean;

begin
  success := False;

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

  http := THttp.Create;

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

  resp := THttpResponse.Create;
  success := http.HttpNoBody('HEAD',url,resp);
  if (success = False) then
    begin
      WriteLn(http.LastErrorText);
      Exit;
    end;

  remoteFileSize := resp.ContentLength;

  WriteLn('Downloading ' + 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 := THttp.Create;
  http2 := THttp.Create;
  http3 := THttp.Create;
  http4 := THttp.Create;

  sbRange := TStringBuilder.Create;
  sbRange.SetString('bytes=<range-start>-<range-end>');
  numReplaced := sbRange.ReplaceI('<range-start>',0);
  numReplaced := sbRange.ReplaceI('<range-end>',chunkSize - 1);
  WriteLn(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);
  WriteLn(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);
  WriteLn(sbRange.GetAsString());
  http3.SetRequestHeader('Range',sbRange.GetAsString());

  sbRange.SetString('bytes=<range-start>-');
  numReplaced := sbRange.ReplaceI('<range-start>',3 * chunkSize);
  WriteLn(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 = True) then
        begin
          numLive := numLive + 1;
        end;
      if (task2.Live = True) then
        begin
          numLive := numLive + 1;
        end;
      if (task3.Live = True) then
        begin
          numLive := numLive + 1;
        end;
      if (task4.Live = True) 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() = False) then
    begin
      WriteLn(task1.ResultErrorText);
      numErrors := numErrors + 1;
    end;
  if (task2.GetResultBool() = False) then
    begin
      WriteLn(task2.ResultErrorText);
      numErrors := numErrors + 1;
    end;
  if (task3.GetResultBool() = False) then
    begin
      WriteLn(task3.ResultErrorText);
      numErrors := numErrors + 1;
    end;
  if (task4.GetResultBool() = False) then
    begin
      WriteLn(task4.ResultErrorText);
      numErrors := numErrors + 1;
    end;
  if (numErrors > 0) then
    begin
      task1.Free;
      task2.Free;
      task3.Free;
      task4.Free;
      Exit;
    end;

  //  All downloads were successful.
  //  Compose the file from the parts.
  fac := TFileAccess.Create;
  success := fac.ReassembleFile('qa_output','chunk','dat','qa_output/hamlet.xml');
  if (success = False) then
    begin
      WriteLn(fac.LastErrorText);
    end
  else
    begin
      WriteLn('Success.');
    end;

  task1.Free;
  task2.Free;
  task3.Free;
  task4.Free;

  //  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');
  WriteLn('bSame = ' + bSame);


  http.Free;
  resp.Free;
  http1.Free;
  http2.Free;
  http3.Free;
  http4.Free;
  sbRange.Free;
  fac.Free;

end;

// ---------------------------------------------------------------------------

begin

  try
    RunDemo;
  except
    on E: Exception do
      WriteLn('Unhandled exception: ', E.ClassName, ': ', E.Message);
  end;

  WriteLn;
  {$IFDEF MSWINDOWS}
  WriteLn('Press Enter to exit...');
  ReadLn;
  {$ENDIF}
end.