使用 https 下载协议时出错:// - Delphi

Error downloading protocol with https : // - Delphi

我想知道如何在 Delphi 中使用 https:// 协议下载文件。我正在使用 idhttp ,但是当我点击下载它时 returns 一个错误代码:

-1

点击按钮后,几秒钟后应用程序终止。

uses: IdHTTP, IdSSLOpenSSL;

IdHTTP1 := TIdHTTP.create(nil);
 try
            IdHTTP1.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);  
            IdHTTP1.Request.Accept := 'text/html, */*';
            IdHTTP1.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
            IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded';
            IdHTTP1.HandleRedirects := True;
            IdHTTP1.get(FNomeArq,MS);
            Ms.Seek(0,soFromBeginning);
            header := IdHTTP1.Response.ContentType;
 except on E : EIdHTTPProtocolException do begin
             showmessage(intToStr(IdHTTP1.ResponseCode));
            end;
            on E: EIdSocketError do begin
                showmessage(intToStr(IdHTTP1.ResponseCode));
            end;
            on E: Exception do begin
              showmessage(intToStr(IdHTTP1.ResponseCode));
            end;
 end;

首先,您没有使用所有必需的单位。

uses
    IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL;

这是工作示例,在 Delphi XE7 和 XE8 下测试。 FMX平台。

var
    FResponse: TMemoryStream;

procedure Load;
var
    LUrl: String;
    IdHTTP: TIdHTTP;
    IdSSLIOHandlerSocketOpenSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
    LUrl := 'https://google.lv/';

    IdHTTP := TIdHTTP.Create(nil);
    try
      IdSSLIOHandlerSocketOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
      try
        IdHTTP.IOHandler := IdSSLIOHandlerSocketOpenSSL;
        try
          IdHTTP.Get(LUrl, FResponse);
        except
          { We are hiding error }
        end;
      finally
        IdSSLIOHandlerSocketOpenSSL.Free;
      end;
    finally
      IdHTTP.Free;
    end;
end;

不要忘记使用 FResponse := TMemoryStream.Create;

创建 FResponse

最后:确保您使用的是正确的 OpenSSL 库 对于 Indy 10.6.1.5182,我正在使用:

[WIN][x86] OpenSSL 共享库 1.0.1.7; OpenSSL 工具包 1.0.1g;

libeay32.dll
1 281 024 bytes

ssleay32.dll
270 336 bytes

[WIN][x64] OpenSSL 共享库 1.0.1.7; OpenSSL 工具包 1.0.1g;

libeay32.dll
1 817 088 bytes

ssleay32.dll
371 200 bytes

首先,你的错误处理是错误的。您只显示 TIdHTTP.ResponseCode 属性 值,没有其他任何内容。 ResponseCode 具有有意义值的唯一例外是 EIdHTTPProtocolException(即便如此,您也应该从 EIdHTTPProtocolException.ErrorCode 属性 中获取值)。 ResponseCode 不太可能在任何其他异常情况下被填充,这在您的情况下显然是这种情况。由于您会遇到捕获不同类型异常的麻烦,因此您应该显示对这些特定类型有意义的错误消息,例如:

except
  on E: EIdHTTPProtocolException do begin
    ShowMessage('HTTP request failed'#13 + IntToStr(E.ErrorCode) + ' ' + E.Message);
  end;
  on E: EIdOpenSSLAPISSLError do
  begin
    ShowMessage('OpenSSL API failure'#13 + E.ClassName + #13'Result Code: ' + IntToStr(E.RetCode) + #13'Error Code: ' + IntToStr(E.ErrorCode));
  end;
  on E: EIdOpenSSLAPICryptoError do
  begin
    ShowMessage('OpenSSL crypto failure'#13 + E.ClassName + #13'Error Code: ' + IntToStr(E.ErrorCode));
  end;
  on E: EIdOpenSSLError do
  begin
    ShowMessage('Unknown OpenSSL error'#13 + E.ClassName + #13 + E.Message);
  end;
  on E: EIdSocketError do begin
    ShowMessage('Socket error'#13 + E.ClassName + #13'Error Code: ' + IntToStr(E.LastError) + ' ' + E.Message);
  end;
  on E: Exception do begin
    ShowMessage('Unexpected error'#13 + E.ClassName + #13 + E.Message);
  end;
end;

其次,如果您是 运行 桌面系统(Windows 或 OSX)​​上的应用程序,那么您正在泄漏 TIdSSLIOHandlerSocketOpenSSL 对象,因为没有给它分配 OwnerTIdHTTP 也没有取得它的所有权。如果您 运行 您的应用程序是在移动系统上运行的(iOS 或 Android),您的代码将会崩溃,因为没有引用让 TIdSSLIOHandlerSocketOpenSSL 对象在之后保持活动状态它被创建。 TIdTCPConnection.IOHandlerTIdTCPConnection.Socket 属性在这些平台上使用 weak 引用,因此对象的引用计数不会增加,最终会过早销毁。如果您不分配 Owner,则需要显式释放对象,例如 Zam 的回答所展示的 try/finally

或者,如果您使用的是最新版本的 Indy,则可以利用今年早些时候添加到 TIdHTTP 的新 HTTPS 功能,这样您就不必创建IOHandler 对象显式:

New HTTPS functionality for TIdHTTP

第三,关于您得到的实际异常,EIdOSSLCouldNotLoadSSLLibrary 的意思正是它的名字所暗示的。无法加载 OpenSSL 库。这意味着无法找到 DLL(libeay32.dll 和 ssleay32.dll),或者它们没有导出 Indy 查找的所有内容。可以调用IdSSLOpenSSLHeaders单元中的WhichFailedToLoad()函数查找OpenSSL无法加载的原因:

except
  //...
  on E: EIdOSSLCouldNotLoadSSLLibrary do
  begin
    ShowMessage('Could not load OpenSSL library'#13'Failed to load: ' + WhichFailedToLoad);
  end;
  //...
end;

确保您安装了兼容版本的 OpenSSL DLL,可以是在您的应用程序文件夹中,在 OS 搜索路径上的文件夹中,或者在您通过 [=] 指定给 Indy 的文件夹中IdSSLOpenSSLHeaders 单元中的 30=] 函数。通常,您应该努力始终使用可用的最新 OpenSSL 版本(在撰写本文时为 1.0.2d)。