0

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.

AmigoJack
  • 5,234
  • 1
  • 15
  • 31
  • Does the (incomplete) local file still exist when you restart? The code linked only resumes if the file is partially downloaded already. – mjn Sep 01 '22 at 14:04

1 Answers1

1

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;
George Betsis
  • 450
  • 2
  • 4
  • 1
    In an HTTP request, the `Range` and `Content-Range` headers are not the same thing. Download resumes are handled using the `Range` header, whereas `Content-Range` is used for uploads instead. So you need to use the `Request.Range(s)` property instead of the `Request.ContentRange...` properties. I have downvoted your answer because of that. When you fix that, I will remove the downvote. – Remy Lebeau Sep 01 '22 at 19:39
  • I have changed the code, to use the Request.Range(s) property instead of the Request.ContentRange... properties. – George Betsis Sep 03 '22 at 14:44
  • I removed my downvote, and updated your code to check if the server supports resuming. Note: there is a use-case you are not accounting for (which I didn't add for you). Checking file sizes is not good enough. If the remote file is the same size as the local file, but its bytes have changed, then you have to restart from the beginning. To account for that, look at the `ETag` response header. When downloading the file, save its current ETag. When you want to resume the file later, compare the saved ETag to the current ETag, and if they differ than restart from the beginning. – Remy Lebeau Sep 03 '22 at 16:20