delphi - 使用 Indy for Delphi 下载 https 图像

标签 delphi delphi-10.1-berlin

我看过很多关于如何将 http 图像下载到内存流的帖子,但我在获取 https 图像时遇到了困难。

我希望这就像放入 SSL 处理程序一样简单,但我不断收到以下错误:

Error connecting with SSL. error:14094410:SSL reourtines:SSL3_READ_BYTES:sslv3 alert handshake failure.

我认为我一直在使用的示例网站使用了 SSLv3。

https://apod.nasa.gov/apod/image/1702/NGC1316_MazlinKellerMenaker1024d.jpg

令人烦恼的是,JSON 实际上返回一个 http 节点,但它被重定向到 https 版本

https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY

{
  "copyright": "Steve Mazlin",
  "date": "2017-02-02",
  "explanation": "An example of violence on a cosmic scale, enormous elliptical galaxy NGC 1316 lies about 75 million light-years away toward Fornax, the southern constellation of the Furnace. Investigating the startling sight, astronomers suspect the giant galaxy of colliding with smaller neighbor NGC 1317 seen just above, causing far flung loops and shells of stars. Light from their close encounter would have reached Earth some 100 million years ago. In the deep, sharp image, the central regions of NGC 1316 and NGC 1317 appear separated by over 100,000 light-years. Complex dust lanes visible within also indicate that NGC 1316 is itself the result of a merger of galaxies in the distant past. Found on the outskirts of the Fornax galaxy cluster, NGC 1316 is known as Fornax A. One of the visually brightest of the Fornax cluster galaxies it is one of the strongest and largest radio sources with radio emission extending well beyond this telescopic field-of-view, over several degrees on the sky.  Participate: Take an Aesthetics & Astronomy Survey",
  "hdurl": "http://apod.nasa.gov/apod/image/1702/NGC1316_MazlinKellerMenaker.jpg",
  "media_type": "image",
  "service_version": "v1",
  "title": "NGC 1316: After Galaxies Collide",
  "url": "http://apod.nasa.gov/apod/image/1702/NGC1316_MazlinKellerMenaker1024d.jpg"
}

有什么想法吗?

旁注:我尝试将 SSL 版本更改为 sslvTLSv1_1sslvTLSv1_2,但没有成功。

function TUrlImageReader.ReadImage: TMemoryStream;
var
  ImgMS: TMemoryStream;
  ImgHTTP: TIdHTTP;
  SSLHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  ImgMS := TMemoryStream.Create;
  SSLHandler:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  ImgHTTP:=TIdHTTP.Create(nil);
  ImgHTTP.IOHandler:=SSLHandler;
  SSLHandler.SSLOptions.Method:=sslvSSLv3;
  try
    try
      ImgHTTP.Get(FURL, ImgMS);
    except
      on E: EIdHTTPProtocolException do
        ImgMS.Clear;
    end;
  finally
    ImgHTTP.Free;
    SSLHandler.Free;
  end;
  ImgMS.Position := 0;
  Result := ImgMS;
end;

最佳答案

确保您使用的是最新版本的 Indy 10,其中包含用于处理 TLS 1.2 的所有最新修复。使用当前版本,当我将 SSLOptions.Method 设置为 sslvTLSv1_1sslvTLSv1_2 (以及就像我将 SSLOptions.SSLVersions 设置为 [sslvTLSv1_1, sslvTLSv1_2] 一样)。图像下载正确。

但是,请注意您的代码中存在内存泄漏。如果引发除 IdHTTPProtocolException 之外的任何异常,则不会释放 TMemoryStream 对象。您的代码应该看起来更像这样:

function TUrlImageReader.ReadImage: TMemoryStream;
var
  ImgHTTP: TIdHTTP;
  SSLHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  Result := TMemoryStream.Create;
  try
    ImgHTTP := TIdHTTP.Create(nil);
    try
      SSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create(ImgHTTP);
      SSLHandler.SSLOptions.Method := sslvTLSv1_1; // or sslvTLSv1_2
      // or:
      // SSLHandler.SSLOptions.SSLVersions := [sslvTLSv1_1, sslvTLSv1_2];
      ImgHTTP.IOHandler := SSLHandler;
      try
        ImgHTTP.Get('https://apod.nasa.gov/apod/image/1702/NGC1316_MazlinKellerMenaker1024d.jpg', Result);
      except
        on E: EIdHTTPProtocolException do
          Result.Clear;
      end;
    finally
      ImgHTTP.Free;
    end;
    Result.Position := 0;
  except
    Result.Free;
    raise;
  end;
end;

此外,从技术上讲,您不需要清除流来响应EIdHTTPProtocolException,因为不会向其中写入任何内容,任何响应数据都将被默默丢弃由 TIdHTTP 执行,因为您没有在 TIdHTTP.HTTPOptions 属性中启用 hoNoProtocolErrorExceptionhoWantProtocolErrorContent 标志。

为了避免捕获 EIdHTTPProtocolException,您可以单独启用 hoNoProtocolErrorException 标志,例如:

function TUrlImageReader.ReadImage: TMemoryStream;
var
  ImgHTTP: TIdHTTP;
  SSLHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  Result := TMemoryStream.Create;
  try
    ImgHTTP := TIdHTTP.Create(nil);
    try
      SSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create(ImgHTTP);
      SSLHandler.SSLOptions.Method := sslvTLSv1_1; // or sslvTLSv1_2
      // or:
      // SSLHandler.SSLOptions.SSLVersions := [sslvTLSv1_1, sslvTLSv1_2];
      ImgHTTP.IOHandler := SSLHandler;
      ImgHTTP.HTTPOptions := ImgHTTP.HTTPOptions + [hoNoProtocolErrorException];

      ImgHTTP.Get('https://apod.nasa.gov/apod/image/1702/NGC1316_MazlinKellerMenaker1024d.jpg', Result);
    finally
      ImgHTTP.Free;
    end;
    Result.Position := 0;
  except
    Result.Free;
    raise;
  end;
end;

关于delphi - 使用 Indy for Delphi 下载 https 图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42007268/

相关文章:

windows - 如何防止任务管理器杀死我的程序?

xml - 当我序列化为字符串时,为什么 TXmlDocument 会省略编码?

c# - C#'s most effective counterpart to Delphi' s TStringList 是什么?

delphi - 水平对接表单而不显示表单

android - 正确进行线程化 FTP 下载 (Indy/Android)

delphi - 如何将标签中的某些字符更改为粗体?

delphi - FindDragTarget 未检测到禁用的控件

multithreading - 如果主线程拥有一个临界区,那么尝试获取该临界区的函数是否会阻塞(如果它也在主线程上)?

delphi - 如何解决Delphi内存不足错误?

listview - 如何通过代码使ListView VCL折叠/展开?