如何通过HTTP从Internet检索文件?

时间:2010-06-26 01:01:09

标签: windows delphi

我想从Internet下载文件,InternetReadFile乍一看似乎是一个简单易用的解决方案。实际上,太好了,不可能。实际上,挖掘一下我已经开始看到它实际上存在很多问题。使用此代码时,人们会抱怨各种问题。

可能会出现问题,因为:

  • 应用程序暂时冻结,直到HTTP服务器响应
  • 由于互联网连接断开,应用程序暂时冻结
  • 应用程序因为HTTP服务器永远不会响应而锁定
  • InternetOpen(我刚刚发现这个)必须在应用程序生命周期内只调用一次

我找不到一个关于如何正确而有力地使用它的完整示例。有没有人知道如何在一个单独的线程中实现它并且超时?还有另一种简单的方法可以从Internet上稳健地下载文件。虽然我不想让像Jedi甚至Indy这样的大型图书馆复杂化我的生活。

function GetFileHTTP (const fileURL, FileName: String): boolean;
CONST
  BufferSize = 1024;
VAR
  hSession, hURL: HInternet;
  Buffer: array[1..BufferSize] of Byte;
  BufferLen: DWORD;
  f: File;
  sAppName: string;
begin
//  result := false;
 sAppName := ExtractFileName(Application.ExeName) ;
 hSession := InternetOpen(PChar(sAppName), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0) ;  { be aware that InternetOpen  need only be called once in your application!!!!!!!!!!!!!! }
 TRY
  hURL := InternetOpenURL(hSession, PChar(fileURL), nil, 0, 0, 0) ;
  TRY
   AssignFile(f, FileName) ;
   Rewrite(f, 1) ;
   REPEAT
    InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen);
    BlockWrite(f, Buffer, BufferLen)
   UNTIL BufferLen = 0;
   CloseFile(f) ;
   Result:= True;
  FINALLY
   InternetCloseHandle(hURL)
  end
 FINALLY
  InternetCloseHandle(hSession)
 END;
END;

编辑: 此功能检查Internet连接是否可用。它似乎也适用于Win98。

{  Are we connected to the Internet? }
function IsConnectedToInternet: Boolean;                                        { Call SHELL32.DLL for Win < Win98 otherwise call URL.dll }
var InetIsOffline: function(dwFlags: DWORD): BOOL; stdcall;
begin
 Result:= FALSE;
 if IsApiFunctionAvailable('URL.DLL', 'InetIsOffline', @InetIsOffline)
 then Result:= NOT InetIsOffLine(0)
 else
   if IsApiFunctionAvailable('SHELL32.DLL', 'InetIsOffline', @InetIsOffline)
   then Result:= NOT InetIsOffLine(0)
end;

我正在使用Delphi 7.非常感谢。


编辑:

由于应用程序在首次启动时挂起而失去客户是亏本的完美配方。

将您的代码编写为依赖于Microsoft平台的代码是不好的。您永远不知道客户是否安装了IE版本x.x.

将东西安装到用户的计算机上就像玩枪一样。这会适得其反。

(请点击此处了解详情:http://thesunstroke.blogspot.com/2010/06/programmig-like-there-is-no-ms-windows.html

6 个答案:

答案 0 :(得分:4)

我基本上和你一样。对我来说,它的工作相当完美。

我的代码和代码之间的唯一区别是我有一个INTERNET_FLAG_RELOAD参数来强制从文件而不是缓存下载。你可以尝试一下,看看它是否更好:

  hURL := InternetOpenURL(hSession, PChar(fileURL), nil, 0, INTERNET_FLAG_RELOAD, 0) ; 

下载前请检查互联网连接。这样做:

  dwConnectionTypes := INTERNET_CONNECTION_MODEM
                 + INTERNET_CONNECTION_LAN
                 + INTERNET_CONNECTION_PROXY;
  InternetConnected := InternetGetConnectedState(@dwConnectionTypes, 0);
  if InternetConnected then ...

答案 1 :(得分:2)

这是一些使用Indy的示例代码。此代码适用于Delphi 2010(使用Indy 10?),但Delphi 7的代码类似。我和D7一起使用Indy已经很多年了,并且对它非常满意。我想在D7中我们使用Indy 9.检查你是否需要下载新版本......

如果需要,可以使用OnWork和OnWorkBegin添加进度表。

这段代码我摘录了一个更大的部分,编辑了一下。我没有尝试编译它,但它会给你一个很好的起点。

function Download( const aSourceURL: String;
                   const aDestFileName: String;
                   out   aDownloadResult: TDownloadResult;
                   out   aErrm: String): boolean;
var
  Stream: TMemoryStream;
  IDAntiFreeze: TIDAntiFreeze;
begin
  aDownloadResult := DROther;
  Result := FALSE;
  fIDHTTP := TIDHTTP.Create;
  fIDHTTP.HandleRedirects := TRUE;
  fIDHTTP.AllowCookies := FALSE;
  fIDHTTP.Request.UserAgent := 'Mozilla/4.0';
  fIDHTTP.Request.Connection := 'Keep-Alive';
  fIDHTTP.Request.ProxyConnection := 'Keep-Alive';
  fIDHTTP.Request.CacheControl := 'no-cache';
  IDAntiFreeze := TIDAntiFreeze.Create;

  Stream := TMemoryStream.Create;
  try
    try
      fIDHTTP.Get(aSourceURL, Stream);
      if FileExists(aDestFileName) then
        DeleteFile(PWideChar(aDestFileName));
      Stream.SaveToFile(aDestFileName);
      Result := TRUE;
      aDownloadResult :=drSuccess;
    except
      On E: Exception do
        begin
          Result := FALSE;
          aErrm := E.Message + ' (' + IntToStr(fIDHTTP.ResponseCode) + ')';
        end;
    end;
  finally
    Stream.Free;
    IDAntiFreeze.Free;
    fIDHTTP.Free;
  end;
end;  { Download }

答案 2 :(得分:1)

我个人最喜欢使用WebHttpRequest组件导入“Microsoft WinHTTP Services”类型库:http://yoy.be/item.asp?i142

var
  w:IWebHttpRequest;
  f:TFileStream;  
  os:TOleStream;
begin 
  w:=CoWebHttpRequest.Create;
  w.Open('GET',SourceURL,false);
  w.Send(EmptyParam);
  os:=TOleStream.Create(IUnknown(w.ResponseStream) as IStream);
  f:=TFileStream.Create(DestinationFilePath,fmCreate);
  os.Position:=0;
  f.CopyFrom(os,os.Size);
  f.Free;
  os.Free;
  w:=nil;
end;

答案 3 :(得分:1)

我建议Synapse。它小巧,稳定且易于使用(无需任何外部库)。

来自httpsend.pas

的示例
function HttpGetText(const URL: string; const Response: TStrings): Boolean;
var
  HTTP: THTTPSend;
begin
  HTTP := THTTPSend.Create;
  try
    Result := HTTP.HTTPMethod('GET', URL);
    if Result then
      Response.LoadFromStream(HTTP.Document);
  finally
    HTTP.Free;
  end;
end;

答案 4 :(得分:0)

ExtActns单元提供了下载到文件所需的内容,而不是摆弄WinAPI。

procedure TMainForm.DownloadFile(URL: string; Dest: string); 
var 
  dl: TDownloadURL; 
begin 
  dl := TDownloadURL.Create(self); 
  try 
    dl.URL := URL; 
    dl.FileName := Dest; 
    dl.ExecuteTarget(nil); //this downloads the file 
    dl.Free; 
  except 
    dl.Free; 
  end; 
end; 

在幕后,它使用来自URLMon库的URLDownloadToFile - 它是IE的一部分,因此也是Windows的一部分。

TDownloadURL不会为你处理任何超时 - 看起来URLMon看起来不支持这样的东西,尽管可能会有一些默认超时导致调用失败 - 但你可以使用OnProgress事件在TDownloadURL上,当事情发生时得到通知,然后在另一个线程中做一些事情,如果它自上次回调以来已经太久了。

答案 5 :(得分:0)

使用上述代码的改进版解决。 (它仍然没有解决所有问题 - MS实际上没有实现对服务器超时的完全支持)

The connection does not timeout while downloading file from internet