如何使用Indy 10和OpenSSL通过HTTPS下载文件?

9
我有以下任务:使用HTTPS和身份验证下载文件。Indy似乎是最好的选择,但由于某些原因,到目前为止它还没有起作用。我已经做了以下准备:
  • 一个TIdHTTP组件,用于下载
  • 一个TIdURI组件用于创建URL
  • 一个TIdSSLIOHandlerSocketOpenSSL组件,应该提供安全连接。所需的DLL文件在二进制文件夹中。
网站还需要身份验证,并且我将用户/密码包含在URL中,如下例所示。简而言之,这是代码:
URI := TIdURI.Create('https://test.example.com/');
URI.Username := ParamUserName;
URI.Password := ParamPassword;

HTTP := TIdHTTP.Create(nil);
if URI.Protocol = 'https' then
begin
  IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  IOHandler.SSLOptions.Method := sslvSSLv3;
  HTTP.IOHandler := IOHandler;
end;

HTTP.Get(URI.GetFullURI([ofAuthInfo]), FileStream);

使用这段代码时,我很快就会收到一个“读取超时”(EIdReadTimeout)异常。在浏览器中测试该URL没有问题。有什么想法是缺失的或者我做错了什么?

应用程序和服务器之间是否存在防火墙/代理?“浏览器工作正常”并不意味着存在直接的互联网连接。 - mjn
@mjustin 如果我查看浏览器设置,没有定义代理。此外,WinInet方法在不设置任何代理的情况下工作的事实告诉我那不是问题所在。 - Tihauan
3
在这种情况下,不要使用TIdURI.Username/Password属性。您必须改用TIdHTTP.Username/Password属性。 - Remy Lebeau
3个回答

12

我最终放弃了Indy和OpenSSL,并使用WinInet进行下载。 这是对我有用的代码:

function Download(URL, User, Pass, FileName: string): Boolean;
const
  BufferSize = 1024;
var
  hSession, hURL: HInternet;
  Buffer: array[1..BufferSize] of Byte;
  BufferLen: DWORD;
  F: File;
begin
   Result := False;
   hSession := InternetOpen('', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0) ;

   // Establish the secure connection
   InternetConnect (
     hSession,
     PChar(FullURL),
     INTERNET_DEFAULT_HTTPS_PORT,
     PChar(User),
     PChar(Pass),
     INTERNET_SERVICE_HTTP,
     0,
     0
   );

  try
    hURL := InternetOpenURL(hSession, PChar(URL), nil, 0, 0, 0) ;
    try
      AssignFile(f, FileName);
      Rewrite(f,1);
      try
        repeat
          InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen) ;
          BlockWrite(f, Buffer, BufferLen)
        until BufferLen = 0;
      finally
        CloseFile(f) ;
        Result := True;
      end;
    finally
      InternetCloseHandle(hURL)
    end
  finally
    InternetCloseHandle(hSession)
  end;
end;

关于上面的代码,需要注意一点:应该始终使用INTERNET_FLAG_RELOAD参数来强制从服务器下载而不是缓存。因此,我建议您将代码更改为:hURL := InternetOpenURL(hSession, PChar(fullURL), nil, 0, INTERNET_FLAG_RELOAD, 0) ; - user2858981
Winapi.WinInet 添加到 uses 中。 - James L.

2

我也遇到过相同的问题。将TIdHTTP.ReadTimeout设置为零可以解决我的问题。

...
HTTP.IOHandler := IOHandler;
HTTP.ReadTimeout := 0;

ReadTimeout=0 是默认的超时时间,它代表无限超时。 - Remy Lebeau
是的,但在连接IOHandler之后在代码中进行设置似乎可以阻止EIdReadTimeout异常被抛出。(至少在我的经验中,使用Delphi附带的Indy版本) - jasonpenny

1

除非安装了一些额外的库,否则所有用于安全连接的SSL相关函数都将失败。

1.) 下载库

2.) 解压缩并将两个DLL文件复制到您的项目文件夹中(或者复制到系统PATH中的某个位置)

这样,您在问题中的代码对我来说就可以正常工作了。

--reinhard


谢谢您的回答。我已经准备好了DLL文件,看起来它们已经被正确加载了。读取超时似乎是由其他原因引起的。WinInet方法与同一服务器配合使用。 - Tihauan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接