How to get a file from the Internet via HTTP?

I want to download a file from the Internet, and InternetReadFile is a good and simple solution at first glance. In fact, too good to be true. Indeed, digging a bit, I began to realize that there were actually a lot of problems with him. People complain about all kinds of problems when using this code.

Problems may arise due to the fact that:

  • the application is temporarily suspended until the HTTP server responds
  • application freezes temporarily as internet connections break
  • application is blocked because the HTTP server never responds
  • InternetOpen (I just opened it recently) MUST be called only once during the life of the application

I could not find a complete example of how to use it correctly and reliably. Does anyone have an idea on how to implement it in a separate thread and timeout? There is another easy way to download a file from the Internet. Although I do not want to complicate my life with very large libraries such as the Jedi or even 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; 

Edit: These features check for internet connectivity. It also works on 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; 

I am using Delphi 7. Thanks a lot.


Edit:

Losing customers because the application freezes on first launch is an ideal recipe for losing money.

Writing code for Microsoft platform dependencies is bad. You never know if the client has IE xx installed

Installing material on a user's computer is a game with a weapon. This will have unpleasant consequences.

(more on this here: http://thesunstroke.blogspot.com/2010/06/programmig-like-there-is-no-ms-windows.html )

+4
windows delphi
source share
6 answers

It was decided to use an improved version of the above code. (it still does not solve all the problems - MS does not actually implement full support for server timeout)

Connection does not disconnect when downloading a file from the Internet

0
source

I basically do the same as you. For me it works flawlessly.

The only differences between my code and your code: I have an INTERNET_FLAG_RELOAD parameter to force download from a file, not to the cache. You can try this and see if it works better:

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

Also check your internet connection before downloading. Do it:

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

Here is sample code that uses Indy. This code is for Delphi 2010 (with Indy 10?), But the code for Delphi 7 will be similar. I used Indy for many years with D7 and was very pleased with it. I think we are using Indy 9 in D7. Check if you need to download the new version ...

You can use OnWork and OnWorkBegin to add a progress bar if you need to.

I pulled out this code from a large part, changing it a little. I did not try to compile it, but it will give you a good starting place.

 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
source

My personal favorite uses the WebHttpRequest component to import the Microsoft WinHTTP Services type library: 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; 
+1
source

I recommend Synapse . It is small, stable and easy to use (without any external libraries).

Example from 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;
+1
source

Instead of messing with WinAPI, ExtActns provides only what you need to load into a file.

 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; 

Under the hood, it uses the URLDownloadToFile from the URLMon library, which is part of IE and therefore part of Windows.

TDownloadURL does not handle any timeouts for you - it seems that this is not supported at all in URLMon, although there may be some default timeout due to which the call is interrupted, but you can use the OnProgress event on TDownloadURL to receive a notification when something happens and then do something in another thread if it has been too long since the last callback.

0
source

All Articles