7

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

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

你们推荐使用什么功能?

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

之前有人向我推荐过使用套接字,但 TClientSocket 似乎也没有超时功能。

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

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

谢谢!

4

4 回答 4

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;
于 2010-08-23T15:16:25.200 回答
2

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

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

于 2010-08-23T17:54:04.947 回答
2

感谢GrandmasterB,这是我设法放在一起的代码:(它在我线程的执行块中)

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;
于 2010-08-24T15:31:12.607 回答
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 是本地文件 }

于 2010-08-24T22:42:13.857 回答