How to download a file with resume support using Indy?

281 views Asked by At

I have already tried code from StackOverflow, f.e. Download, pause and resume an download using Indy components.

But when I try to use IdHttp1.Disconnect() in order to pause download and then resume it, the download does not resume. The file is downloaded from zero. So, what should I do in order to add resume support to my Delphi app?

BTW: the server allows download resuming, because IDM can resume the download.

1

There are 1 answers

3
George Betsis On

You have to check the size of the remote file, and the size of the local file (if exists). If the size of the remote file is larger than the size of the local file, then you have to set the Ranges property of the Request property of TIdHTTP to the starting point (the local file size).

For example, if the remote file size is 100000 bytes, and the local file size is 80000 bytes, then you have to set http.Request.Ranges to start at 80000.

Here is a fully working code for you to use:

function FileSize(const FileName: string): Int64;
var
  AttributeData: TWin32FileAttributeData;
begin
  if GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @AttributeData) then
  begin
    Int64Rec(Result).Lo := AttributeData.nFileSizeLow;
    Int64Rec(Result).Hi := AttributeData.nFileSizeHigh;
  end
  else
    Result := -1;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  http: TIdHTTP;
  FS: TFileStream;
  file_url: String;
  file_path: String;
  web_file_size: Integer;
  local_file_size: Integer;
  IhaveToDownload: Boolean;
begin
  file_url := 'http://212.183.159.230/50MB.zip';
  file_path := 'C:\50MB.zip';
  IhaveToDownload := False;

  FS := nil;
  http := TIdHTTP.Create(nil);
  try
    //I will check if local file exists
    if FileExists(file_path) then
    begin
      //I have to run http.Head() to get the ContentLength of the remote file.
      http.Head(file_url);

      //web_file_size now has the remote file size in bytes
      web_file_size := http.Response.ContentLength;
      //local_file_size now has the local file size in bytes
      local_file_size := FileSize(file_path);

      //check if remote file is bigger than local file (so I have to resume download)
      if (web_file_size > local_file_size) and (http.Response.AcceptRanges = 'bytes') then
      begin
        //remote file is bigger than local file, so I will resume download.
        http.Request.Ranges.Add.StartPos := local_file_size;
        FS := TFileStream.Create(file_path, fmOpenReadWrite);
        FS.Position := local_file_size;
        IhaveToDownload := True;
      end
      else if web_file_size <> local_file_size then
      begin
        //remote file is not equal with local file, so I will restart download.
        FS := TFileStream.Create(file_path, fmCreate);
        IhaveToDownload := True;
      end;
    end
    else
    begin
      //File does not exist locally. I will create a new local file
      FS := TFileStream.Create(file_path, fmCreate);
      IhaveToDownload := True;
    end;

    if IhaveToDownload then
      http.Get(file_url, FS);
  finally
    FS.Free;
    http.Free;
  end;
end;