Downloading a file from the Internet while being able to abort the download any time Downloading a file from the Internet while being able to abort the download any time multithreading multithreading

Downloading a file from the Internet while being able to abort the download any time


This works.

var  DownloadAbort: boolean;procedure Download(const URL, FileName: string);var  hInet: HINTERNET;  hURL: HINTERNET;  Buffer: array[0..1023] of AnsiChar;  BufferLen, amt: cardinal;  f: file;const  UserAgent = 'Delphi Application'; // Change to whatever you wishbegin  DownloadAbort := false;  hInet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);  try    hURL := InternetOpenUrl(hInet, PChar(URL), nil, 0, 0, 0);    try      FileMode := fmOpenWrite;      AssignFile(f, FileName);      try        Rewrite(f, 1);        repeat          InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen);          BlockWrite(f, Buffer[0], BufferLen, amt);          if DownloadAbort then            Exit;        until BufferLen = 0;      finally        CloseFile(f);      end;    finally      InternetCloseHandle(hURL);    end;  finally    InternetCloseHandle(hInet);  end;end;

In practice, the code above will be part of your thread's Execute method, and you do not need DownloadAbort; intead, you check

if Terminated then  Exit;


TWinSocketStream has a timeout. You basically create a blocking socket, and then create a TWinSocketStream using that socket for reading/writing. (sort of like a using a StreamWriter). Then you loop until the connection is closed, the thread is terminated, or you reach the number of expected bytes. There's a WaitForData() which allows you to check for incoming data w/ a timeout, so you're never 'locked' past that timeout value.

You would, though, have to handle the http process yourself. ie, send the 'GET', and then read the response header to determine how many bytes to expect, but thats not too difficult.


Thanks to GrandmasterB, here is the code I've managed to put together: (it's in my thread's Execute block)

var  Buffer: array [0..1024 - 1] of Char;  SocketStream: TWinSocketStream;  RequestString: String;  BytesRead: Integer;begin  try    ClientSocket.Active := true;    DownloadedData := '';    FillChar(Buffer, SizeOf(Buffer), #0);    if not Terminated then    begin      // Wait 10 seconds      SocketStream := TWinSocketStream.Create(ClientSocket.Socket, 10000);      try        // Send the request to the webserver        RequestString := 'GET /remotedir/remotefile.html HTTP/1.0'#13#10 +          'Host: www.someexamplehost.com'#13#10#13#10;        SocketStream.Write(RequestString[1], Length(RequestString));        // We keep waiting for the data for 5 seconds        if (not Terminated) and SocketStream.WaitForData(5000) then          repeat            // We store the data in our buffer            BytesRead := SocketStream.Read(Buffer, SizeOf(Buffer));            if BytesRead = 0 then              break            else              DownloadedData := DownloadedData + Buffer;          until Terminated or (not SocketStream.WaitForData(2000));      finally        // Close the connection        SocketStream.Free;        if ClientSocket.Active then          ClientSocket.Active := false;      end;    end;  except  end;

You have to put this to the constructor of the thread:

  ClientSocket := TClientSocket.Create(nil);  ClientSocket.Host := 'www.someexamplehost.com';  ClientSocket.Port := 80;  ClientSocket.ClientType := ctBlocking;  inherited Create(false);  // CreateSuspended := false;

And this to the desctructor:

  ClientSocket.Free;  inherited;