使用 Indy 下载 https 图像 Delphi

Download https image with Indy for Delphi

我看过很多关于如何将 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 实际上 returns 一个 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;

确保您使用的是 up-to-date 版本的 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;

此外,从技术上讲,您不需要 Clear 流来响应 EIdHTTPProtocolException,因为不会向其中写入任何内容,任何响应数据都会被 [=21 静默丢弃=] 因为您没有在 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;