zoukankan      html  css  js  c++  java
  • Using WinInet functions to download a file asynchronously in Delphi

     [转自]http://www.neugls.info/?p=191

    (注:不好意思,如果你已经应用了该代码的话,请注意,在HttpQueryInfoA函数之前加上一个Reservered:=0;不然,会不能正确的获取到文件的大小。)

    WinInet functions were used in windows to help developers develop network application more conveniently, but there is no Delphi  code example on the internet, so I give some code here, help it useful for you.

    Please first look at the following code:

    type
      TNTDownLoadProgressCallBack = reference to procedure(Current,
        Total: Cardinal);
      TNTDownLoadFinishedCallBack = reference to procedure(Status: NativeInt);
      TNTShouldExit=reference to function():Boolean;
    
    procedure DownLoadToFile(
                  const URL, SavePath: string;
                  ProgressCallBack: TNTDownLoadProgressCallBack;
                  FinishCallBack: TNTDownLoadFinishedCallBack;
                  CanExit:TNTShouldExit
               );
    const
      USER_EXIT_DOWNLOAD_PROCESS=$666666;
    
    
    implementation
    
    uses 
    {$IFDEF VER230}
    	Winapi.Windows, System.SysUtils, Winapi.WinInet
    {$ELSE}
    	Windows, SysUtils,WinInet
    {$ENDIF};
    
    
    
    var
      Header: String = // 'GET %s HTTP/1.1'+sLineBreak+
        'Host: %s' + sLineBreak +
        'Connection: keep-alive' + sLineBreak +
        'User-Agent: NeuglsWorkStudio-Auto-updater' + sLineBreak +
        'Accept: text/html,application/xhtml+xml,application/*;q=0.9,*/*;q=0.8' +sLineBreak +
        'Accept-Encoding: gzip,deflate,sdch' + sLineBreak +
        'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3' + sLineBreak +
        'Accept-Language: *' + sLineBreak + 'Referer: http://neuglsworkstudio.com/';
    
    var
      RequestHandle: HINTERNET;
      ConnetHandle: HINTERNET;
    
      ConnectEvent: THandle;
      RequestOpendEvent:THandle;
      RequestCompleteEvent: THandle;
      ShouldExit:Boolean;
      TheExitCode:Cardinal;
    
    procedure InternetStatusCallback(hInt: HINTERNET; dwContext: DWORD_PTR;
        dwInternetStatus: DWORD; lpvStatusInformation: LPVOID;
        dwStatusInformationLength: DWORD); stdcall;
      var
        InternetAsyncResult: TInternetAsyncResult;
      begin
        case dwContext of
            1: if (dwInternetStatus = INTERNET_STATUS_HANDLE_CREATED) then
                begin
                   InternetAsyncResult:=TInternetAsyncResult(lpvStatusInformation^);
                   ConnetHandle:=Pointer(InternetAsyncResult.dwResult);
                   SetEvent(ConnectEvent);
                end;
            2: case dwInternetStatus of
                  INTERNET_STATUS_HANDLE_CREATED:
                    begin
                      InternetAsyncResult:=TInternetAsyncResult(lpvStatusInformation^);
                      RequestHandle:=Pointer(InternetAsyncResult.dwResult);
                      SetEvent(RequestOpendEvent);
                    end;
                  INTERNET_STATUS_REQUEST_COMPLETE:
                    begin
                      SetEvent(RequestCompleteEvent);
                    end;
               end;
        end;
      end;
    
    procedure DownLoadToFile(const URL, SavePath: string;
      ProgressCallBack: TNTDownLoadProgressCallBack;
      FinishCallBack: TNTDownLoadFinishedCallBack;
      CanExit:TNTShouldExit);
    {$IFDEF MSWINDOWS}
    const
      BufferSize = 1024*4;
    var
      Session: HINTERNET;
      FHeader: AnsiString;
      dwReceived: Cardinal;
      Reservered: Cardinal;
      Buffer: PAnsiChar;
      dwBufferLength: Cardinal;
      BOK: Boolean;
      FileStream: TFileStream;
      InternetBuffer: TInternetBuffersA;
      CallBackPointer: PFNInternetStatusCallback;
    
      dwFileSize: Cardinal;
      dwSize,: Cardinal;
      I:Cardinal;
    
    
    label
      ToExit;
    
      function GetHost(TheURL: string): String;
      var
        FURL: String;
      begin
        FURL := TheURL + '555';
        if pos(UpperCase('http://'), UpperCase(FURL)) > 0 then
        begin
          Delete(FURL, 1, Length('http://'));
        end;
        Result := Copy(FURL, 1, pos('/', FURL) - 1);
      end;
    
      function GetURI():string;
      var
        s:String;
      begin
        S:=GetHost(URL) ;
        Result := Copy(URL, pos(s, URL) + Length(s) + 1, MaxInt);
      end;
    
    
    begin
      {Init the event}
      ConnectEvent:=CreateEvent(nil,false,false,'ConnectEvent');
      RequestCompleteEvent:=CreateEvent(nil,false,false,'RequestCompleteEvent');
      RequestOpendEvent:= CreateEvent(nil,false,false,'requestOpen');
    
      Session := InternetOpenA(PAnsiChar(AnsiString('NWSDownloader')),
        INTERNET_OPEN_TYPE_PRECONFIG, niL, niL, INTERNET_FLAG_ASYNC);
      if not Assigned(Session) then
        goto ToExit;
    
      CallBackPointer := @InternetStatusCallback;
      CallBackPointer := InternetSetStatusCallback(Session, CallBackPointer);
      if NativeInt(CallBackPointer) = INTERNET_INVALID_STATUS_CALLBACK then
        raise Exception.Create('callback function is not valid');
    
      ConnetHandle:=InternetConnectA(
                      Session,
                      PAnsiChar(AnsiString(GetHost(URL))),
                      INTERNET_DEFAULT_HTTP_PORT,
                      nil,
                      nil,
                      INTERNET_SERVICE_HTTP,
                      0,
                      1);
      if not Assigned(ConnetHandle) then
      begin
        if GetLastError=ERROR_IO_PENDING then
          WaitForSingleObject(ConnectEvent,INFINITE) //wait connection complete.
        else
          goto ToExit;
      end;
    
      RequestHandle:=HttpOpenRequestA(ConnetHandle,
                                   PAnsiChar('GET'),
                                   PAnsiChar(AnsiString(GetURI())),
                                   nil,
                                   nil,
                                   nil,
                                   INTERNET_FLAG_RELOAD or INTERNET_FLAG_NO_CACHE_WRITE,
                                   2);
      if not Assigned(RequestHandle) then
      begin
         if GetLastError=ERROR_IO_PENDING then
            WaitForSingleObject(RequestOpendEvent,INFINITE) //wait connection complete.
         else
          goto ToExit;
      end;
    
    
    
    
      FHeader := AnsiString(Format(Header, [GetHost(URL)]));
      if not HttpSendRequestA(RequestHandle,
                             PAnsiChar(FHeader),
                             SizeOf(AnsiChar)*Length(FHeader),
                             nil,
                             0)
      then
        if GetLastError<>ERROR_IO_PENDING then
           Goto ToExit;
    
      WaitForSingleObject(RequestCompleteEvent,INFINITE); //wait request complete.
    
      //get Content-Length
      dwFileSize:=0;
      dwSize:= Sizeof(dwFileSize);
      Reservered:=0;
      HttpQueryInfoA(
                RequestHandle,
                HTTP_QUERY_CONTENT_LENGTH or HTTP_QUERY_FLAG_NUMBER,
                @dwFileSize,
                dwSize,Reservered
       );
    
    
      GetMem(Buffer, BufferSize);
      ZeroMemory(@InternetBuffer,SizeOf(InternetBuffer));
      FileStream := TFileStream.Create(SavePath, fmCreate);
      dwReceived := 0;
      I:=0;
      TheExitCode:=0;
      ShouldExit:=False;
      try
    
        while (True) do
        begin
          ZeroMemory(@InternetBuffer,SizeOf(InternetBuffer));
          InternetBuffer.dwStructSize := SizeOf(InternetBuffer);
          InternetBuffer.lpvBuffer := Buffer;
          InternetBuffer.dwBufferLength := BufferSize;
    
          ResetEvent(RequestCompleteEvent);
          Reservered:=1;
          BOK := InternetReadFileExA(RequestHandle, @InternetBuffer, IRF_NO_WAIT,
            Reservered);
          if BOK then
          begin
            Inc(I);
            FileStream.Write(Buffer^, InternetBuffer.dwBufferLength);
            ZeroMemory(Buffer, BufferSize);
            dwReceived := dwReceived + InternetBuffer.dwBufferLength;
            if I mod 3=0 then
              ProgressCallBack(dwReceived, dwFileSize);
          end
          else
          begin
            if GetLastError=ERROR_IO_PENDING then
              WaitForSingleObject(RequestCompleteEvent,INFINITE); //wait request complete.
          end;
          if (InternetBuffer.dwBufferLength=0) and(dwReceived=dwFileSize) then
            Break;
          if ShouldExit then
            Break;
          if CanExit then
          begin
            TheExitCode:=USER_EXIT_DOWNLOAD_PROCESS;
            Break;
          end;
        end;
      finally
        FreeMem(Buffer);
        FileStream.Free;
      end;
      InternetCloseHandle(RequestHandle);
      InternetCloseHandle(ConnetHandle);
      InternetSetStatusCallback(Session, nil);
      InternetCloseHandle(Session);
      FinishCallBack(TheExitCode);
      Exit;
    ToExit:
      FinishCallBack(GetLastError);
      Exit;
    {$ENDIF}
    end;
    

    If you want to know much more about why the code should like this, you may visit the following website pages:

  • 相关阅读:
    使用Navicat for Oracle新建表空间、用户及权限赋予---来自烂泥
    NonAction与ChildActionOnly
    C# Monitor的Wait和Pulse方法使用详解
    机械键盘简介
    【转载】 中小型研发团队架构实践
    BinaryReader 自己写序列化
    显式接口实现
    AssemblyVersion和AssemblyFileVersion的区别
    自定义设置程序集版本重定向和程序集位置的信息
    单例模式中的属性实现
  • 原文地址:https://www.cnblogs.com/neugls/p/2245041.html
Copyright © 2011-2022 走看看