• Wininet下载类初稿


    unit uWnDownClass;

    interface

    uses
      Windows,Messages,SysUtils,Classes,WinInet;

    const
      WM_HTTPCOMM_PROGRESS = WM_USER + 1700;
      InnerAgent = 'Mozilla/4.0 (compatible; MSIE 6.0; Win32)';
      HttpVersion = 'HTTP/1.1';
      D_C_T = 'Content-Type:application/x-www-form-urlencoded';
      D_C_T_S = Length(D_C_T);
      BUFFER_SIZE = 4096;

    type
      //错误类型,没有错误为wwecNil
      TWinInetErrorCauses = (wwecNil,                             //0
                             wwecAttemptConnect,                  //1
                             wwecOpen,                            //2
                             wwecConnect,                         //3
                             wwecOpenRequest,                     //4
                             wwecConfigureRequest,                //5
                             wwecExecRequest,                     //6
                             wwecEndRequest,                      //7
                             wwecTimeOut,                         //8
                             wwecUPD,                             //9
                             wwecAbort,                           //10
                             wwecStatus,                          //11
                             wwecContentLength,                   //12
                             wwecContentType,                     //13
                             wwecReadFile,                        //14
                             wwecWriteFile);                      //15

      TProxyInfo = record
      public
        FProxyType : integer; //-1: preConfig 0: noproxy 1: sock4 2: sock5 3: http
        FProxyServer : String;
        FProxyPort : integer;
        FProxyUserName : String;
        FProxyUserPass : String;
      end;
      TOnDownloadProgress = procedure(const ASize,ATotal: Int64) of object;
      TWnDownClass = class
      private
        FAbort: Boolean;
        //FhNotify: HWND;
        FResponse: TMemoryStream;
        //FKeepConnected: Boolean;
        FNet: HINTERNET;
        FRequest: HINTERNET;
        FSession: HINTERNET;
        FProxyInfo: TProxyInfo;
        FProxy: string;
        FServerPort: integer;
        FServerName: string;
        FEncodeUrl: string;
        FVerb: string;
        FHttpHeader: string;
        //FpUserData: Pointer;
        FSecure: Boolean;
        FTimeOut: Integer;
        FErrorCause: TWinInetErrorCauses;
        FWininetStateChanged: Boolean;
        FErrInfo: string;
        FServerPass: string;
        FServerUser: string;
        FData: array[0..BUFFER_SIZE] of Char;
        FStatus: Integer;
        FContentType: string;
        FContentLength: Int64;
        FTotal: Int64;
        FFileSize: Int64;
        FOnDownloadProgress: TOnDownloadProgress;
        procedure SetAbort(const Value: Boolean);
        procedure FixServerInfo;
        procedure FixProxyServerInfo;
        function OpenConnection: Boolean;
        function OpenRequest: Boolean;
        function ConfigureRequest: Boolean;
        function PerformGet: Boolean;
        procedure AssignError(AError: TWinInetErrorCauses);

        function DetectProxyServer: DWORD;
        function PortToUse(APort: Integer): Integer;
        function FetchHeader(AFlags: Integer): Boolean;
        function FixContentLength: Boolean;  //获取接受数据的大小
        function FixContentType: Boolean;  //获取接受数据的类型
        function ReadResponse: Boolean;    //读取接受数据
        function FixWinINetError(AError: Integer): string;
        procedure HookDataReadSized;
        procedure SetOnDownloadProgress(const Value: TOnDownloadProgress);
      public
        constructor Create;
        destructor Destroy;override;
        property Abort: Boolean read FAbort write SetAbort;
        //property hNotify:HWND read FhNotify write FhNotify;
        property Response: TMemoryStream read FResponse;
        property  ServerName: string read FServerName write FServerName;
        property  ServerPort: integer read FServerPort write FServerPort;
        property  ServerUser: string read FServerUser write FServerUser;
        property  ServerPass: string read FServerPass write FServerPass;
        property ProxyInfo: TProxyInfo read FProxyInfo write FProxyInfo;
        property HttpHeader: string read FHttpHeader write FHttpHeader;
        property Status: Integer read FStatus;
        property ContentLength: Int64 read FContentLength;
        property ContentType: string read FContentType;
        property FileSize: Int64 read FFileSize write FFileSize;
        property ErrInfo: string read FErrInfo;
        property ErrorCause: TWinInetErrorCauses read FErrorCause;
        property OnDownloadProgress: TOnDownloadProgress  read FOnDownloadProgress write SetOnDownloadProgress;
        procedure CleanUp(isAll: Boolean);
        function HttpGet(isUrl:string;iiTimeout:integer;ASecure:Boolean = False):boolean;
      end;


    implementation

    uses
      HTTPApp;


    //HTTP通讯过程中的状态回调函数
    procedure StatusCallback(ASession: hInternet; AContext, AIS: DWord; AInfo:
      Pointer; ASIN: DWord); stdcall;
    //var
    //  AReason: TWinInetCallBackReason;
    //  lpHostContext: PInternetCallbackContext;
    begin
      { TODO : 回调函数 }
      {case AIS of
        INTERNET_STATUS_RESOLVING_NAME: AReason := wwcbrResolving;
        INTERNET_STATUS_NAME_RESOLVED: AReason := wwcbrResolved;
        INTERNET_STATUS_CONNECTING_TO_SERVER: AReason := wwcbrConnecting;
        INTERNET_STATUS_CONNECTED_TO_SERVER: AReason := wwcbrConnected;
        INTERNET_STATUS_SENDING_REQUEST: AReason := wwcbrWriting;
        INTERNET_STATUS_REQUEST_SENT: AReason := wwcbrWritten;
        INTERNET_STATUS_RECEIVING_RESPONSE: AReason := wwcbrReading;
        INTERNET_STATUS_RESPONSE_RECEIVED: AReason := wwcbrRead;
        INTERNET_STATUS_CLOSING_CONNECTION: AReason := wwcbrClosing;
        INTERNET_STATUS_CONNECTION_CLOSED: AReason := wwcbrClosed;
      else Exit;
      end;
      lpHostContext := PInternetCallbackContext(AContext);
      if Assigned(lpHostContext^.OnSelfCallBack) then begin
        lpHostContext^.OnSelfCallBack(AReason);
      end;
      if Assigned(lpHostContext^.OnCallBack) then begin
        lpHostContext^.OnCallBack(AReason);
      end; }

    end;

    { TWnDownClass }

    procedure TWnDownClass.AssignError(AError: TWinInetErrorCauses);
    var
      I, H: Integer;
      LTemp: string;
      LR: Cardinal;
    begin
      FErrorCause := AError;
      if Length(FErrInfo) = 0 then
      begin
        LR := GetLastError;
        if (LR < 12000) or (LR < 12175) then
        begin
          H := GetModuleHandle('wininet.dll');
          SetLength(LTemp, 256);
          I := FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, Pointer(H), LR, 0,
            PChar(LTemp), 256, nil);
          SetLength(LTemp, I);
          FErrInfo := 'Error '+IntTostr(LR)+':'+LTemp;
        end
        else
          FErrInfo := 'Error '+IntTostr(LR)+':'+SysErrorMessage(GetLastError);
      end;
    end;

    procedure TWnDownClass.CleanUp(isAll: Boolean);
    begin
      if isAll then
      begin
        if Assigned(FRequest) then
        begin
          InternetCloseHandle(FRequest);
          FRequest := nil;
        end;
        if Assigned(FSession) then
        begin
          InternetCloseHandle(FSession);
          FSession := nil;
        end;
        if Assigned(FNet) then
        begin
          InternetCloseHandle(FNet);
          FNet := nil;
        end;
      end;
      //FResponse.Clear;
      SetLength(FProxy,0);
    end;

    function TWnDownClass.ConfigureRequest: Boolean;
      function SetUPD(AOption: DWORD; AUPD: PChar): Boolean;
      begin
        Result := (Length(AUPD) =0)
          or InternetSetOption(FRequest, AOption, AUPD, Length(AUPD));
      end;
    begin
      Result := False;
      if FAbort then Exit;
      //设置HTTP头
      if FFileSize >0 then
      begin
        if Length(FHttpHeader) > 0 then
          FHttpHeader := FHttpHeader + #13#10'Range: bytes='+ IntToStr(FFileSize) +'-'#13#10
        else
          FHttpHeader := 'Range: bytes='+ IntToStr(FFileSize) +'-'#13#10;
      end;
      if Length(FHttpHeader) > 0 then
      begin
        Result := HttpAddRequestHeaders(FRequest, PWideChar(FHttpHeader),
          Cardinal(-1), HTTP_ADDREQ_FLAG_ADD or HTTP_ADDREQ_FLAG_REPLACE);

        if not Result then
        begin
          AssignError(wwecConfigureRequest);
          Exit;
        end;
      end;
      //设置超时
      if (FTimeOut < 1) or (FTimeOut > 30) then FTimeOut := 30;
      FTimeOut := FTimeOut * 1000;
      Result := InternetSetOption(FNet, INTERNET_OPTION_CONNECT_TIMEOUT,
          @FTimeOut, SizeOf(Integer)) and
        InternetSetOption(FNet, INTERNET_OPTION_RECEIVE_TIMEOUT, @FTimeOut,
          SizeOf(Integer)) and
        InternetSetOption(FNet, INTERNET_OPTION_SEND_TIMEOUT, @FTimeOut,
          SizeOf(Integer));

      if not (Result) then
      begin
        AssignError(wwecTimeOut);
        Exit;
      end;
      //设置代理用户密码,访问用户密码
      if SetUPD(INTERNET_OPTION_PROXY_USERNAME,PChar(FProxyInfo.FProxyUserName))
        and SetUPD(INTERNET_OPTION_PROXY_PASSWORD,PChar(FProxyInfo.FProxyUserPass))
        and SetUPD(INTERNET_OPTION_USERNAME,PChar(fServerPass))
        and SetUPD(INTERNET_OPTION_PASSWORD,PChar(FServerUser))
        then
      else
        AssignError(wwecUPD);

    end;

    constructor TWnDownClass.Create;
    begin
      inherited;
      FResponse := TMemoryStream.Create;
      FRequest := nil;
      FSession := nil;
      FNet := nil;
      //FKeepConnected := False;
      FAbort := False;
      FWininetStateChanged := False;
      FErrInfo := '';
      FEncodeUrl := '';
      FServerUser := '';
      FServerPass := '';
      FVerb := 'GET';
      FStatus := -1;
      FFileSize := 0;
      SetLength(FProxy,0);
    end;

    destructor TWnDownClass.Destroy;
    begin
      FResponse.Free;
      inherited;
    end;

    function TWnDownClass.DetectProxyServer: DWORD;
    begin
       //-1: preConfig 0: noproxy 1: sock4 2: sock5 3: http
      //Result:
      //INTERNET_OPEN_TYPE_PRECONFIG                   0
      //INTERNET_OPEN_TYPE_DIRECT                      1
      //INTERNET_OPEN_TYPE_PROXY                       3
      //INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY 4
      with FProxyInfo do
      case (FProxyType-1) of
        0: Result := INTERNET_OPEN_TYPE_DIRECT;
        1:
        begin
          Result := INTERNET_OPEN_TYPE_PROXY;
          FProxy := Format('socks=%s:%s',[FProxyServer,Inttostr(FProxyPort)]);
        end;
        2:
        begin
          Result := INTERNET_OPEN_TYPE_PROXY;
          FProxy := Format('socks5=%s:%s',[FProxyServer,Inttostr(FProxyPort)]);
        end;
        3:
        begin
          Result := INTERNET_OPEN_TYPE_PROXY;
          FProxy := Format('%s:%s',[FProxyServer,Inttostr(FProxyPort)]);
        end;
      else
          Result := INTERNET_OPEN_TYPE_PRECONFIG;
      end;
    end;
    function TWnDownClass.FetchHeader(AFlags: Integer): Boolean;
    var
      BufLen, Index: DWORD;
    begin
      Result := False;
      if FAbort then Exit;
      Index := 0;
      BufLen := BUFFER_SIZE;
      FillChar(FData,BufLen,0);
      Result := HttpQueryInfo(FRequest, AFlags, @FData, BufLen, Index);
    end;

    function TWnDownClass.FixContentLength: Boolean;
    var
      LTemp: string;
    begin
      Result := False;
      if FAbort then Exit;
      Result := FetchHeader(HTTP_QUERY_CONTENT_LENGTH);
      LTemp := FData;
      if Result then
        FContentLength := StrToInt64Def(LTemp,0)
      else
        AssignError(wwecContentLength);
    end;

    function TWnDownClass.FixContentType: Boolean;
    begin
      Result := False;
      if FAbort then Exit;
      Result := FetchHeader(HTTP_QUERY_CONTENT_TYPE);
      if Result then
        FContentType := FData
      else
        AssignError(wwecContentType);
    end;

    procedure TWnDownClass.FixProxyServerInfo;
    var
      ls1ServerName, lsPort: string;
      liLoc: Integer;
    begin
      try
        ls1ServerName := LowerCase(FProxyInfo.FProxyServer);
        liLoc := Pos(':', ls1ServerName);
        if liLoc = 0 then Exit;
        lsPort := Copy(ls1ServerName, liLoc + 1, Length(ls1ServerName) - liLoc);
        FProxyInfo.FProxyServer := PChar(Copy(ls1ServerName, 1, liLoc - 1));
        FProxyInfo.FProxyPort := StrToIntDef(lsPort,FProxyInfo.FProxyPort);
      except
      end;
    end;

    procedure TWnDownClass.FixServerInfo;
    var
      ls1ServerName, lsPort: string;
      liLoc: Integer;
    begin
      try
        ls1ServerName := LowerCase(FServerName);
        liLoc := Pos(':', ls1ServerName);
        if liLoc = 0 then Exit;
        lsPort := Copy(ls1ServerName, liLoc + 1, Length(ls1ServerName) - liLoc);
        FServerName := PChar(Copy(ls1ServerName, 1, liLoc - 1));
        FServerPort := StrToIntDef(lsPort,FServerPort);
      except
      end;
    end;

    function TWnDownClass.FixWinINetError(AError: Integer): string;
    {var
      I, H: Integer;
    begin
      H := GetModuleHandle('wininet.dll');
      SetLength(Result, 256);
      I := FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, Pointer(H), AError, 0,
        PChar(Result), 256, nil);
      SetLength(Result, I);
    end; }
    begin
      Result := 'Http Status: '+ IntToStr(AError);
    end;

    procedure TWnDownClass.HookDataReadSized;
    //var
      //nTransPercent: Integer;
    begin
      //if IsWindow(hNotify) then
      //begin

      if Assigned(FOnDownloadProgress) then
        FOnDownloadProgress(FTotal + FFileSize,FContentLength + FFileSize);

      //  PostMessage(hNotify, WM_HTTPCOMM_PROGRESS, Integer(pUserData),
      //    nTransPercent);
      //end;
    end;

    function TWnDownClass.HttpGet(isUrl: string; iiTimeout: integer; ASecure: Boolean): boolean;
    begin
      SetLastError(0);
      FErrInfo := '';
      FErrorCause := wwecNil;
      Result := False;
      FSecure := ASecure;
      FTimeOut := iiTimeout;
      FTotal := 0;
      { TODO : 不知道是否需要UTF8编码 }
      //FEncodeUrl := isUrl;  //EncodeUrlUtf8(FEncodeUrl);
      FEncodeUrl := HttpEncode(UTF8Encode(isUrl));
      FVerb := 'GET';
      FixServerInfo;
      FixProxyServerInfo;
      Result := OpenConnection
      and OpenRequest
      and ConfigureRequest
      and PerformGet;
      CleanUp(True);
    end;

    function TWnDownClass.OpenConnection: Boolean;
    var
      LProxyType: DWORD;

      function WW_AttemptConnect: Boolean;
      begin
        Result := (CompareText(FServerName, 'localhost') = 0) or
          (InternetAttemptConnect(0) = ERROR_SUCCESS);
        if not (Result) then AssignError(wwecAttemptConnect);
      end;

      procedure CancelMaxConnectLimite();
      var
        liPerServer1, liPerServer2: Integer;
      begin
        try
          liPerServer1 := 5;
          liPerServer2 := 10;
          //INTERNET_OPTION_MAX_CONNS_PER_SERVER  73
          InternetSetOption(nil, 73, @liPerServer1, SizeOf(Integer));
          //INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER  74
          InternetSetOption(nil, 74, @liPerServer2, SizeOf(Integer));
        except
        end;
      end;

      function WW_InternetOpen: Boolean;
      var
        ltInfo: INTERNET_CONNECTED_INFO;
      begin
        FNet := InternetOpen(PChar(InnerAgent), LProxyType, PChar(FProxy), nil, 0);

        Result := (FNet <> nil);
        if Result then begin
          try
            if not FWininetStateChanged then begin
              //INTERNET_OPTION_CONNECTED_STATE  50
              //取消IE的脱机状态
              ltInfo.dwConnectedState := INTERNET_STATE_CONNECTED;
              ltInfo.dwFlags := 0;          // ISO_FORCE_DISCONNECTED;
              InterNetSetOption(FNet, INTERNET_OPTION_CONNECTED_STATE, @ltInfo, SizeOf(ltInfo));
            end;
          except
          end;
          //InternetSetStatusCallBack(FNet, @StatusCallBack);
          //INTERNET_OPTION_HTTP_DECODING

          if InternetSetOption(FNet, 65, @Result, 1) then begin
            Beep;
          end;
        end else begin
          AssignError(wwecOpen);
        end;
      end;

      function WW_InternetConnect: Boolean;
      var
        context: dword;
      begin
        //同步通讯设置
        context := 0;
        //异步通讯需要设置特定值
        //FCallBackContext.CallbackID := 0;
        //context:=dword(@FCallBackContext);
        FSession := InternetConnect(FNet, PChar(FServerName),
            PortToUse(FServerPort), '', '', INTERNET_SERVICE_HTTP, 0, context);
        Result := (FSession <> nil);
        if not (Result) then AssignError(wwecConnect);
      end;

    begin
      Result := False;
      if FAbort then Exit;
      if WW_AttemptConnect then
      begin
        LProxyType := DetectProxyServer;
        SetLastError(0);
        if not FWininetStateChanged then CancelMaxConnectLimite();
        Result := WW_InternetOpen and WW_InternetConnect;
        FWininetStateChanged := True;
      end;
    end;

    function TWnDownClass.OpenRequest: Boolean;
    var
      context,ATimeOut, dwFlags: DWORD;
    begin
      Result := False;
      if FAbort then Exit;
      context := 0;
      if FSecure then
      begin
        FRequest := HTTPOpenRequest(FSession, PChar(FVerb),
          PChar(FEncodeUrl), PChar(HttpVersion), nil, nil, INTERNET_FLAG_KEEP_CONNECTION or
          INTERNET_FLAG_SECURE or SECURITY_FLAG_IGNORE_UNKNOWN_CA or
          SECURITY_FLAG_IGNORE_CERT_CN_INVALID or
          SECURITY_FLAG_IGNORE_CERT_DATE_INVALID, context);
        ATimeOut := 0;
        dwFlags := 0;

        if (FRequest <> nil) and
          (not InternetQueryOption(FRequest, INTERNET_OPTION_SECURITY_FLAGS,
          Pointer(@ATimeOut), dwFlags)) then
        begin
          GetLastError;
        end;
      end
      else
      begin
        FRequest := HTTPOpenRequest(FSession, PChar(FVerb),
          PChar(FEncodeUrl), PChar(HttpVersion), nil, nil, {Ord(FSecure) * INTERNET_FLAG_SECURE or}
          INTERNET_FLAG_NO_CACHE_WRITE or INTERNET_FLAG_RELOAD or
          INTERNET_FLAG_KEEP_CONNECTION , context);
      end;
      Result := (FRequest <> nil);
      if not (Result) then AssignError(wwecOpenRequest);
    end;

    function TWnDownClass.PerformGet: Boolean;
    var
      AtimeOut, dwFlags: DWORD;
      //LErr: Cardinal;
    begin
      Result := False;
      if FAbort then Exit;
      Result := HTTPSendRequest(FRequest, nil, 0, nil, 0);
      //Result := HTTPSendRequest(FRequest, D_C_T, D_C_T_S, nil, 0);
      if not (Result) then
      begin
        if GetLastError = ERROR_INTERNET_INVALID_CA then //WinInet 无效证书颁发机构错误
        begin
          ATimeOut := 0;
          dwFlags := 0;
          InternetQueryOption(FRequest, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@ATimeOut), dwFlags);
          dwFlags := dwFlags or SECURITY_FLAG_IGNORE_UNKNOWN_CA;
          InternetSetOption(FRequest, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, SizeOf(Integer));
          Result := HTTPSendRequest(FRequest, nil, 0, nil, 0);
        end
        else
        begin
          AssignError(wwecExecRequest);
          Exit;
        end;
      end;
      Result := FetchHeader(HTTP_QUERY_STATUS_CODE);
      if not Result then
      begin
        AssignError(wwecStatus);
        Exit;
      end;
      FStatus := StrToIntDef(FData, -1);
      if (FStatus = HTTP_STATUS_OK) or (FStatus = HTTP_STATUS_PARTIAL_CONTENT) then
      begin
        Result := FixContentLength and FixContentType and ReadResponse;
      end
      else
      begin
        FErrInfo := FixWinINetError(FStatus);
        AssignError(wwecStatus);
      end;
    end;

    function TWnDownClass.PortToUse(APort: Integer): Integer;
    begin
      if APort > 0 then
        Result := APort
      else
        if FSecure then
          Result := INTERNET_DEFAULT_HTTPS_PORT
        else
          Result := INTERNET_DEFAULT_HTTP_PORT;
    end;

    function TWnDownClass.ReadResponse: Boolean;
    var
      ASize, ARead: DWORD;
      ABuffer: Pointer;
    begin
      Result := False;
      if FAbort then Exit;
      FResponse.Clear;
      ASize := BUFFER_SIZE;
      FTotal := 0;
      ABuffer := AllocMem(ASize);
      try
        HookDataReadSized;
        repeat
          Result := InternetReadFile(FRequest, ABuffer, ASize, ARead);
          if not Result then
          begin
            AssignError(wwecReadFile);
            Break;
          end;
          if (ARead > 0) then
          begin
            FResponse.WriteBuffer(ABuffer^, ARead);
            Inc(FTotal, ARead);
            HookDataReadSized;
          end;
        until ((ARead = 0) or FAbort);
      finally
        FreeMem(ABuffer, 0);
      end;
    end;

    procedure TWnDownClass.SetAbort(const Value: Boolean);
    begin
      FAbort := Value;
      if FAbort then
      begin
        FErrorCause := wwecAbort;
        FErrInfo := 'User Download Abouted';
      end;
    end;

    procedure TWnDownClass.SetOnDownloadProgress(const Value: TOnDownloadProgress);
    begin
      FOnDownloadProgress := Value;
    end;

    end.
  • 相关阅读:
    short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
    SpringMVC常用的注解有哪些?
    Spring支持的ORM?
    什么是代理?
    一对一、一对多的关联查询 ?
    iHTML 的 form 提交之前如何验证数值文本框的内容全部为数字?
    解释JDBC抽象和DAO模块?
    Bean 工厂和 Application contexts 有什么区别?
    GitHub的注册
    HTML的学习
  • 原文地址:https://www.cnblogs.com/enli/p/2217204.html
Copyright © 2020-2023  润新知