使用wininet下载文件时如何避免冻结

Posted

技术标签:

【中文标题】使用wininet下载文件时如何避免冻结【英文标题】:how to avoid freezing when download file using wininet 【发布时间】:2015-05-09 13:38:40 【问题描述】:

我使用wininet下载图像并将其保存到内存流这是我的程序

procedure DownloadToStream(const Url: string; ms: TMemoryStream);
var
  hSession     : HINTERNET;
  hService     : HINTERNET;
  lpBuffer     : array[0..1023] of Byte;
  dwBytesRead  : DWORD;
  dwBytesAvail : DWORD;
  dwTimeOut    : DWORD;
begin
  hSession := InternetOpen('usersession', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if not Assigned(hSession) then Exit;
  try
    hService := InternetOpenUrl(hSession, PChar(Url), nil, 0, 0, 0);
    if hService = nil then
      Exit;
    try
      dwTimeOut := 60000;
      InternetSetOption(hService, INTERNET_OPTION_RECEIVE_TIMEOUT, @dwTimeOut, SizeOf(dwTimeOut));
      if InternetQueryDataAvailable(hService, dwBytesAvail, 0, 0) then
      repeat
        if not InternetReadFile(hService, @lpBuffer[0], SizeOf(lpBuffer), dwBytesRead) then
          Break;
        if dwBytesRead <> 0 then
          ms.WriteBuffer(lpBuffer[0], dwBytesRead);
      until dwBytesRead = 0;
    finally
      InternetCloseHandle(hService);
    end;
  finally
    InternetCloseHandle(hSession);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ms: TMemoryStream;
begin
  ms := TMemoryStream.Create;
  try
    DownloadToStream('imageurl, ms);
    ms.Position := 0;
    //do whatever you want with ms
  finally
    ms.Free;
  end;
end;

在下载小图像时它工作正常并且没有冻结,但是当它涉及到大图像时,应用程序在下载完成之前一直冻结,如何避免这种情况?

【问题讨论】:

您应该花一点时间了解 Windows 消息循环的工作原理,并观察您的函数是从消息循环中调用的。 如果您想阅读有关 windows 消息循环的好文章,这是一篇很棒的文章:edn.embarcadero.com/article/38447 非常感谢您的指导,我在重复直到处理冻结之间使用了Application.ProcessMessages; @DelphiStudent Now... 请注意:delphi.about.com/od/objectpascalide/a/… ProcessMessages 是一个快速而肮脏的修复,但它是肮脏的编程,如果你正在构建除混搭测试应用程序之外的任何东西最终会回来咬你。 是的,你是对的,我可能会将代码从 DownloadToStream 移动到线程 OnExecute 事件 【参考方案1】:

您的程序正在冻结,因为在下载完成之前调用不会返回,而且正如您从网页浏览中知道的那样,有时这可能需要很长时间。发生这种情况时,您的程序没有运行正常的事件循环和处理输入。

如果您想让它正常工作,您需要在不同的线程中进行下载。多线程可能非常棘手,但使用正确的库会变得简单得多。对于这样的事情,我建议查看the Async/Await functionality in OmniThreadLibrary.

【讨论】:

谢谢你的回答,如果我用idhttp会容易得多? @DelphiStudent:不是这样。 TIdHTTP 仍然同步下载东西。如果要保持 UI 响应,则需要异步执行长时间运行的任务。 OmniThreadLibrary 确实可以帮助您做到这一点。 Indy 不是也支持多线程,也就是说它的代码可以在额外的线程中执行吗?我知道 TIdTCPServer 支持处理接收到的命令,但我不确定其他 Indy 组件。

以上是关于使用wininet下载文件时如何避免冻结的主要内容,如果未能解决你的问题,请参考以下文章

如何使用winsock或wininet下载文件[关闭]

使用 Wininet 下载二进制文件

WinInet 可以在不重新开始的情况下恢复文件下载吗?

Delphi - 使用 Wininet 下载文件?

如何在 WinInet C++ 中获取 FTP 下载的进度

使用wininet与服务器交换数据