如何在TThread中正确使用Idhttp?
How to use Idhttp within TThread Properly?
我目前有这个 TThread 可以将一些图像下载到桌面,这是线程代码:
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;
这里是调用 Create Thread 的表单
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;
当我调用应用程序 Destroy 时:
if Assigned(Downloadanimation) then
begin
Downloadanimation.Terminate;
FreeAndNil(Downloadanimation);
end;
但是每次我在下载 Thread Fired 后关闭应用程序时都会出现 运行 时间错误。这是因为我同时下载了多张图片吗?如果是的话,有没有更好的方法来编写线程,比如在下载图像完成后等待,如果这是真正的问题,然后开始新的下载。
发出线程终止信号后,在 释放它之前 对其调用 WaitFor()
:
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'
调用 TIdHTTP.Get()
时。如果请求的资源存在但不是 GIF,服务器 应该 报告 406 Not Acceptable
响应,其他任何内容都会报告 HTTP 错误,就像 TIdHTTP.Head()
一样。在此示例中发送 HEAD
请求没有意义。
我目前有这个 TThread 可以将一些图像下载到桌面,这是线程代码:
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;
这里是调用 Create Thread 的表单
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;
当我调用应用程序 Destroy 时:
if Assigned(Downloadanimation) then
begin
Downloadanimation.Terminate;
FreeAndNil(Downloadanimation);
end;
但是每次我在下载 Thread Fired 后关闭应用程序时都会出现 运行 时间错误。这是因为我同时下载了多张图片吗?如果是的话,有没有更好的方法来编写线程,比如在下载图像完成后等待,如果这是真正的问题,然后开始新的下载。
发出线程终止信号后,在 释放它之前 对其调用 WaitFor()
:
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'
调用 TIdHTTP.Get()
时。如果请求的资源存在但不是 GIF,服务器 应该 报告 406 Not Acceptable
响应,其他任何内容都会报告 HTTP 错误,就像 TIdHTTP.Head()
一样。在此示例中发送 HEAD
请求没有意义。