如何在TThread中正确使用Idhttp?

时间:2016-08-09 22:29:04

标签: delphi delphi-xe8 idhttp

我目前有这个TThread将一些图像下载到桌面,这是线程代码:

CheckComboBox

这里是调用Create Thread

的Form
type
  TDownloadUpdateAnimateEvent = procedure(Sender: TObject; AAnimationname: String;
    var AAnimationUrl: String) of object;

type
  TDownloadanimation = class(TThread)
    private
      FOnUpdateAnimate: TDownloadUpdateAnimateEvent;
      FAnimationname : String;
      FAnimationUrl : string;
      FPathImage : String;
      ImageName: string;
      PathURL: string;
      FFileNameImage: string;
    procedure DoUpdateAnimate;
    protected
      procedure Execute; override;
    public
      constructor Create(AAnimationname:string; AAnimationUrl: string; AOnUpdateAnimate : TDownloadUpdateAnimateEvent; APathImage : string);
      property PathImage: string read FPathImage;
      property FileNameImage: string read FFileNameImage;
  end;

{ TDownloadanimation }

constructor TDownloadanimation.Create(AAnimationname, AAnimationUrl: string; AOnUpdateAnimate : TDownloadUpdateAnimateEvent; APathImage : string);
var
  URI: TIdURI;
begin
  inherited Create(false);
  FOnUpdateAnimate := AOnUpdateAnimate;
  FPathImage := APathImage;
  FAnimationname := AAnimationname;
  FAnimationUrl := AAnimationUrl;
  URI := TIdURI.Create(FAnimationUrl);
  try
    ImageName := URI.Document;
    PathURL := URI.path;
  finally
    FreeAndNil(URI);
  end;
end;

procedure TDownloadanimation.DoUpdateAnimate;
begin
  if Assigned(FOnUpdateAnimate) then
    FOnUpdateAnimate(self, FAnimationname, FFileNameImage);
end;

procedure TDownloadanimation.Execute;
var
  aMs: TMemoryStream;
  aIdHttp: TIdHttp;
  IdSSL: TIdSSLIOHandlerSocketOpenSSL;
  path: string;
  dir: string;
  SPEXT : String;
  itsimage: string;
  responsechk: Integer;
begin
  dir := AnsiReplaceText(PathURL, '/', '');

  if (ImageName = '') then
  begin
    exit;
  end;

  path := PathImage + ImageName;

  if fileexists(path) then
  begin
    FFileNameImage := path;
    if Assigned(FOnUpdateAnimate) then
    begin
      Synchronize(DoUpdateAnimate);
    end;
    exit;
  end
  else
    if not fileexists(path) then
    begin
      aMs := TMemoryStream.Create;
      aIdHttp := TIdHttp.Create(nil);
      IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
      try
        IdSSL.SSLOptions.Method := sslvTLSv1;
        IdSSL.SSLOptions.Mode := sslmUnassigned;
        aIdHttp.HTTPOptions := [hoForceEncodeParams] + [hoNoProtocolErrorException];
        aIdHttp.IOHandler := IdSSL;
        aIdHttp.AllowCookies := True;
        aIdHttp.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
        aIdHttp.HandleRedirects := True;
        aIdHttp.RedirectMaximum := 3;
        try
          aIdHttp.Head(trim(FAnimationUrl));
        except
        end;
        itsimage := aIdHttp.Response.ContentType;
        responsechk := aIdHttp.ResponseCode;

        if responsechk <> 200 then
        begin
          FFileNameImage := 'error';
          if Assigned(FOnUpdateAnimate) then
          begin
            Synchronize(DoUpdateAnimate);
          end;
          exit;
        end;
        if (itsimage = 'image/gif') then
        begin
          try
            aIdHttp.Get(trim(FAnimationUrl), aMs);
          except
          end;
          aMs.SaveToFile(path);
        end;

        try
          if aIdHttp.Connected then
            aIdHttp.Disconnect;
        except
        end; 

     finally
       FreeAndNil(aMs);
       FreeAndNil(IdSSl);
       FreeAndNil(aIdHttp);
     end;
  end;

  FFileNameImage := path;

  if Assigned(FOnUpdateAnimate) then
  begin
    Synchronize(DoUpdateAnimate);
  end;
end;

当应用程序Destroy我打电话:

For i := 0 To imageslist.Count-1 do
begin
  Animatref := 'ref';
  Animaturl := imageslist.Strings[i];
  URI := TIdURI.Create(Animaturl);
  try
    ImageName := URI.Document;
  finally
    FreeAndNil(URI);
  end;

  if (ExtractFileExt(ImageName) = '.gif') then
  begin
    if Fileexists(CheckPath+ImageName) then //if image exists then do something and dont start the Thread To download it 
    begin
      //do something
    end 
    else 
    if NOT Fileexists(CheckPath+ImageName) then // not found on desk and start to download
    begin
      addanimation(Animatref, Animaturl);
    end;
  end;
end;


procedure Tform1.addanimation(animationname, animationurl: string);
var
  Pathanimate:string;
begin
  Pathanimate := appfolder;
  Downloadanimation := TDownloadanimation.Create(animationname, animationurl,  UpdateAnimate, Pathanimate);
end;

但是每次下载Thread Fired后关闭应用程序时都会出现运行时错误。这是因为我同时下载了多张图片吗?如果是这样有更好的方式来编写线程,如下载图像完成后等待,然后开始新的下载,如果这是真正的问题。

1 个答案:

答案 0 :(得分:4)

在您发出终止线程的信号后,在之前调用{strong 1}} 释放它:

WaitFor()

此外,您的Form的逻辑可能同时运行多个下载线程,但您只跟踪创建的最后一个线程。只有在应用程序退出时,您才能在完成后释放它。您应该在每个线程上设置if Assigned(Downloadanimation) then begin Downloadanimation.Terminate; Downloadanimation.WaitFor; // <-- add this FreeAndNil(Downloadanimation); end; ,或者将所有线程存储在FreeOnTerminate=True中并使用他们的TList事件来了解每个线程何时完成,以便您可以将其从列出并释放它。

另外,您的线程的OnTerminated逻辑可以通过完全删除对Execute()的调用来减少其网络流量,而是将TIdHTTP.Head()属性设置为致电TIdHTTP.Request.Accept'image/gif'。如果请求的资源存在但不是GIF,服务器应该报告TIdHTTP.Get()响应,其他任何事件都会报告HTTP错误,就像406 Not Acceptable会有的那样。在此示例中发送TIdHTTP.Head()请求毫无意义。