从Internet下载文件,同时可以随时中止下载

时间:2010-08-23 14:27:10

标签: multithreading delphi download delphi-7 wininet

我想从专门用于下载的单独线程中下载Delphi程序中的文件。

问题是主程序可以随时关闭(因此下载线程也可以随时终止)。因此,即使没有连接或服务器滞后宝贵的秒钟,我还需要一种方法在一两秒内终止下载线程。

你们建议使用什么功能?

我已尝试使用InterOpenURL / InternetReadFile,但它没有超时参数。它有一个异步版本,但我找不到任何正在运行的Delphi示例,我不确定异步版本是否会保护我免受挂起......

以前建议使用套接字,但TClientSocket似乎也没有超时功能。

我需要做好充分的准备,因此如果用户在他/她的计算机上遇到Internet连接问题,或者网络服务器一直滞后,我的应用程序在关闭之前就不会挂起。

在回答我不想使用任何第三方组件和Indy时请记住。非常感谢任何示例代码。

谢谢!

4 个答案:

答案 0 :(得分:5)

这很有效。

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 wish
begin
  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;

在实践中,上面的代码将成为线程的Execute方法的一部分,而您不需要DownloadAbort;你正在检查

if Terminated then
  Exit;

答案 1 :(得分:2)

TWinSocketStream超时。您基本上创建了一个阻塞套接字,然后使用该套接字创建一个TWinSocketStream进行读/写。 (有点像使用StreamWriter)。然后循环直到连接关闭,线程终止,或者达到预期的字节数。有一个WaitForData()允许您检查传入的数据是否超时,因此您永远不会“锁定”超过该超时值。

但是,您必须自己处理http进程。即发送'GET',然后读取响应头以确定预期的字节数,但这并不太难。

答案 2 :(得分:2)

感谢GrandmasterB,这里是我设法组建的代码:(它在我的线程的Execute块中)

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;

你必须将它放到线程的构造函数中:

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

  inherited Create(false);  // CreateSuspended := false;

这是针对结构的:

  ClientSocket.Free;
  inherited;

答案 3 :(得分:1)

来自delphi.about.com


uses ExtActns, ...

type
   TfrMain = class(TForm)
   ...
   private
     procedure URL_OnDownloadProgress
        (Sender: TDownLoadURL;
         Progress, ProgressMax: Cardinal;
         StatusCode: TURLDownloadStatus;
         StatusText: String; var Cancel: Boolean) ;
   ...

implementation
...

procedure TfrMain.URL_OnDownloadProgress;
begin
   ProgressBar1.Max:= ProgressMax;
   ProgressBar1.Position:= Progress;
end;

function DoDownload;
begin
   with TDownloadURL.Create(self) do
   try
     URL:='http://0.tqn.com/6/g/delphi/b/index.xml';
     FileName := 'c:\ADPHealines.xml';
     OnDownloadProgress := URL_OnDownloadProgress;

     ExecuteTarget(nil) ;
   finally
     Free;
   end;
end;

{ 注意: URL属性指向Internet FileName是本地文件 }