使用 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
对象,因为没有给它分配 Owner
,TIdHTTP
也没有取得它的所有权。如果您 运行 您的应用程序是在移动系统上运行的(iOS 或 Android),您的代码将会崩溃,因为没有引用让 TIdSSLIOHandlerSocketOpenSSL
对象在之后保持活动状态它被创建。 TIdTCPConnection.IOHandler
和 TIdTCPConnection.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)。
我想知道如何在 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;
最后:确保您使用的是正确的 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
对象,因为没有给它分配 Owner
,TIdHTTP
也没有取得它的所有权。如果您 运行 您的应用程序是在移动系统上运行的(iOS 或 Android),您的代码将会崩溃,因为没有引用让 TIdSSLIOHandlerSocketOpenSSL
对象在之后保持活动状态它被创建。 TIdTCPConnection.IOHandler
和 TIdTCPConnection.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)。