Using WinInet to identify total file size before downloading it

后端 未结 3 1729
我寻月下人不归
我寻月下人不归 2020-12-09 20:54

I got the source below from a third-party site explaining how to download a file from the internet using WinInet. I\'m not too familiar with API, and I took at

相关标签:
3条回答
  • 2020-12-09 21:25

    You can use the HEAD method and check the Content-Length to retrieve the file size of a remote file

    Check these two Methods

    WinInet

    If you want execute a HEAD method you must use the HttpOpenRequest, HttpSendRequest and HttpQueryInfo WinInet functions .

    uses
     SysUtils,
     Windows,
     WinInet;
    
    function GetWinInetError(ErrorCode:Cardinal): string;
    const
       winetdll = 'wininet.dll';
    var
      Len: Integer;
      Buffer: PChar;
    begin
      Len := FormatMessage(
      FORMAT_MESSAGE_FROM_HMODULE or FORMAT_MESSAGE_FROM_SYSTEM or
      FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_IGNORE_INSERTS or  FORMAT_MESSAGE_ARGUMENT_ARRAY,
      Pointer(GetModuleHandle(winetdll)), ErrorCode, 0, @Buffer, SizeOf(Buffer), nil);
      try
        while (Len > 0) and {$IFDEF UNICODE}(CharInSet(Buffer[Len - 1], [#0..#32, '.'])) {$ELSE}(Buffer[Len - 1] in [#0..#32, '.']) {$ENDIF} do Dec(Len);
        SetString(Result, Buffer, Len);
      finally
        LocalFree(HLOCAL(Buffer));
      end;
    end;
    
    
    procedure ParseURL(const lpszUrl: string; var Host, Resource: string);
    var
      lpszScheme      : array[0..INTERNET_MAX_SCHEME_LENGTH - 1] of Char;
      lpszHostName    : array[0..INTERNET_MAX_HOST_NAME_LENGTH - 1] of Char;
      lpszUserName    : array[0..INTERNET_MAX_USER_NAME_LENGTH - 1] of Char;
      lpszPassword    : array[0..INTERNET_MAX_PASSWORD_LENGTH - 1] of Char;
      lpszUrlPath     : array[0..INTERNET_MAX_PATH_LENGTH - 1] of Char;
      lpszExtraInfo   : array[0..1024 - 1] of Char;
      lpUrlComponents : TURLComponents;
    begin
      ZeroMemory(@lpszScheme, SizeOf(lpszScheme));
      ZeroMemory(@lpszHostName, SizeOf(lpszHostName));
      ZeroMemory(@lpszUserName, SizeOf(lpszUserName));
      ZeroMemory(@lpszPassword, SizeOf(lpszPassword));
      ZeroMemory(@lpszUrlPath, SizeOf(lpszUrlPath));
      ZeroMemory(@lpszExtraInfo, SizeOf(lpszExtraInfo));
      ZeroMemory(@lpUrlComponents, SizeOf(TURLComponents));
    
      lpUrlComponents.dwStructSize      := SizeOf(TURLComponents);
      lpUrlComponents.lpszScheme        := lpszScheme;
      lpUrlComponents.dwSchemeLength    := SizeOf(lpszScheme);
      lpUrlComponents.lpszHostName      := lpszHostName;
      lpUrlComponents.dwHostNameLength  := SizeOf(lpszHostName);
      lpUrlComponents.lpszUserName      := lpszUserName;
      lpUrlComponents.dwUserNameLength  := SizeOf(lpszUserName);
      lpUrlComponents.lpszPassword      := lpszPassword;
      lpUrlComponents.dwPasswordLength  := SizeOf(lpszPassword);
      lpUrlComponents.lpszUrlPath       := lpszUrlPath;
      lpUrlComponents.dwUrlPathLength   := SizeOf(lpszUrlPath);
      lpUrlComponents.lpszExtraInfo     := lpszExtraInfo;
      lpUrlComponents.dwExtraInfoLength := SizeOf(lpszExtraInfo);
    
      InternetCrackUrl(PChar(lpszUrl), Length(lpszUrl), ICU_DECODE or ICU_ESCAPE, lpUrlComponents);
    
      Host := lpszHostName;
      Resource := lpszUrlPath;
    end;
    
    function GetRemoteFileSize(const Url : string): Integer;
    const
      sUserAgent = 'Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101';
    
    var
      hInet    : HINTERNET;
      hConnect : HINTERNET;
      hRequest : HINTERNET;
      lpdwBufferLength: DWORD;
      lpdwReserved    : DWORD;
      ServerName: string;
      Resource: string;
      ErrorCode : Cardinal;
    begin
      ParseURL(Url,ServerName,Resource);
      Result:=0;
    
      hInet := InternetOpen(PChar(sUserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
      if hInet=nil then
      begin
        ErrorCode:=GetLastError;
        raise Exception.Create(Format('InternetOpen Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
      end;
    
      try
        hConnect := InternetConnect(hInet, PChar(ServerName), INTERNET_DEFAULT_HTTP_PORT, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
        if hConnect=nil then
        begin
          ErrorCode:=GetLastError;
          raise Exception.Create(Format('InternetConnect Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
        end;
    
        try
          hRequest := HttpOpenRequest(hConnect, PChar('HEAD'), PChar(Resource), nil, nil, nil, 0, 0);
            if hRequest<>nil then
            begin
              try
                lpdwBufferLength:=SizeOf(Result);
                lpdwReserved    :=0;
                if not HttpSendRequest(hRequest, nil, 0, nil, 0) then
                begin
                  ErrorCode:=GetLastError;
                  raise Exception.Create(Format('HttpOpenRequest Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
                end;
    
                 if not HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH or HTTP_QUERY_FLAG_NUMBER, @Result, lpdwBufferLength, lpdwReserved) then
                 begin
                  Result:=0;
                  ErrorCode:=GetLastError;
                  raise Exception.Create(Format('HttpQueryInfo Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
                 end;
              finally
                InternetCloseHandle(hRequest);
              end;
            end
            else
            begin
              ErrorCode:=GetLastError;
              raise Exception.Create(Format('HttpOpenRequest Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
            end;
        finally
          InternetCloseHandle(hConnect);
        end;
      finally
        InternetCloseHandle(hInet);
      end;
    
    end;
    

    Indy

    Also check this code using indy.

    function GetRemoteFilesize(const Url :string) : Integer;
    var
      Http: TIdHTTP;
    begin
      Http := TIdHTTP.Create(nil);
      try
        Http.Head(Url);
        result:= Http.Response.ContentLength;
      finally
        Http.Free;
      end;
    end;
    
    0 讨论(0)
  • 2020-12-09 21:36

    after fixing the types it looks better like this:

    function GetContentLength(URLHandle:HINTERNET):Int64;
    // returns the expected download size.  Returns -1 if one not provided
    var
     SBufferSize, srv:Cardinal;
    begin
     srv:=0;
     SBufferSize:=20;
     if Not HttpQueryInfo(URLHandle, HTTP_QUERY_CONTENT_LENGTH or HTTP_QUERY_FLAG_NUMBER, {@SBuffer} @Result, SBufferSize, srv) then Result:=-1;
    end;
    

    to call it:

    {get the file handle}
    hURL:=InternetOpenURL(hSession, PChar(URL), nil, 0, 0, 0);
    if hURL=Nil then
    begin
     InternetCloseHandle(hSession);
     ShowMessage('The link is incorrect!');
     exit;
    end;
    {get the file size}
    filesize:=GetContentLength(hURL);
    
    0 讨论(0)
  • 2020-12-09 21:45

    Answering the question of how to get a download size with WinInet. This is out of one of my file downloaders that is based on WinInet.

    This is the method I use to get the download size:

    function TWebDownloader.GetContentLength(URLHandle: HINTERNET): Int64;
    // returns the expected download size.  Returns -1 if one not provided
       var
         SBuffer: Array[1..20] of char;
         SBufferSize: Integer;
         srv: integer;
       begin
         srv := 0;
        SBufferSize := 20;
        if HttpQueryInfo(URLHandle, HTTP_QUERY_CONTENT_LENGTH, @SBuffer, SBufferSize, srv) then
           Result := StrToFloat(String(SBuffer))
        else
           Result := -1;
       end;
    

    Use of this method requires an open request handle, and does NOT require reading any of the data:

     URLHandle := HttpOpenRequest(ConnectHandle, 'GET', Pchar(sitepath), nil,
                      nil, nil, INTERNET_FLAG_NO_CACHE_WRITE, 0);
     ...
     DownloadSize := GetContentLength(URLHandle);
    

    HTH

    0 讨论(0)
提交回复
热议问题