{******************************************************************************
文件名称: uWnDownLoadHelper.pas
文件功能: 断点续传下载线程单元
作者 : enli
--------------------------------------------------------------------------------}
文件名称: uWnDownLoadHelper.pas
文件功能: 断点续传下载线程单元
作者 : enli
--------------------------------------------------------------------------------}
unit
uWnDownLoadHelper;
interface
uses
IdHTTP, IdAuthentication, IdHeaderList, IniFiles, md5,
SysUtils, IdAuthenticationSSPI, IdAuthenticationDigest, Classes, IdException,
IdSocks, IdIOHandlerSocket, DateUtils, IdComponent, Forms,ucchttpClient;
const
S_TMP_POSTFIX = ' .tmp ' ;
S_INI_POSTFIX = ' .sni ' ;
type
TWnDownloadProgress = procedure (APosition,ACount: Int64) of object ;
// 代理设置结构
TWnProxySetting = packed record
FIsProxyEnabled: Boolean;
FSocksVersion: TSocksVersion;
FProxyHost: string ;
FProxyPort: Word;
FAuthUserName: string ;
FAuthPassword: string ;
FAuthDomain: string ;
end ;
PWnProxySetting = ^TWnProxySetting;
TWnDownLoadThread = class (TThread)
private
FHttp: TCCHTTPClient;
FProxy: TWnProxySetting;
FTarget: string ;
FSaveFile: string ;
FTempFile: string ;
FProgress: TWnDownloadProgress;
FCurrentFileSize,FFileSize : Int64;
FSaveLocation: string ;
FOnLog: TDolog;
FWarningMsg: string ;
FReLoad: Boolean;
procedure SetProxy( const Value: PWnProxySetting);
function GetProxy :PWnProxySetting;
procedure SetHttpProxy;
procedure SetTarget( const Value: string );
procedure SetProgress( const Value: TWnDownloadProgress);
procedure DoBeginWork(Sender: TObject; const APos,ACount: Int64);
procedure DoWork(Sender: TObject; const AWork:Int64);
procedure DoEndWork(Sender: TObject);
procedure DoUpdateUI;
procedure SetSaveLocation( const Value: string );
procedure SaveIniFile;
procedure DelIniFile;
procedure DoLog( const AMsg: string );
procedure DoSycLog;
function GetTempFile: string ;
procedure SetOnLog( const Value: TDolog);
procedure SetReLoad( const Value: Boolean);
public
procedure Execute; override ;
constructor Create;
destructor Destroy; override ;
class procedure chunkedConvertToFile( const ASource,ADestination: string );
property PProxy:PWnProxySetting read GetProxy write SetProxy;
property Target: string read FTarget write SetTarget;
property SaveLocation : string read FSaveLocation write SetSaveLocation;
property Progress:TWnDownloadProgress read FProgress write SetProgress;
property ReLoad:Boolean read FReLoad write SetReLoad;
property OnLog: TDolog read FOnLog write SetOnLog;
end ;
procedure DownLoadFile( const AUrl,ASaveFile: string ;AProxy:PWnProxySetting;AProgress:TWnDownloadProgress;ALog:TDolog;AReLoad:Boolean);
implementation
procedure DownLoadFile( const AUrl,ASaveFile: string ;AProxy:PWnProxySetting;AProgress:TWnDownloadProgress;ALog:TDolog;AReLoad:Boolean);
var
LThread: TWnDownLoadThread;
begin
LThread : = TWnDownLoadThread.Create;
LThread.ReLoad : = AReLoad;
LThread.Target : = AUrl;
LThread.SaveLocation : = ASaveFile;
LThread.PProxy : = AProxy;
LThread.Progress : = AProgress;
LThread.OnLog : = ALog;
LThread.Resume;
end ;
{ TWnDownLoadThread }
class procedure TWnDownLoadThread.chunkedConvertToFile( const ASource,
ADestination: string );
var
LSStream,LDStream: TFileStream;
LBuff: Byte;
LFlag: Boolean;
LTemp : string ;
I,LCount : Integer;
begin
if not FileExists(ASource) then Exit;
if FileExists(ADestination) then Exit;
LSStream : = TFileStream.Create(ASource,fmOpenRead );
LDStream : = TFileStream.Create(ADestination,fmCreate);
try
LSStream.Seek( 0 , 0 );
LFlag : = True;
I : = 0 ;
while LFlag do
begin
LSStream.Read(LBuff,SizeOf(Byte));
Inc(I);
if (LBuff = $0A) and (I > 2 ) and (LTemp[I - 1 ] = #$0D) then begin
LCount : = StrToIntDef( ' $ ' + trim(LTemp), 1 );
if LCount = 1 then
begin
LCount : = 0 ;
Break;
// raise 异常
end ;
if LCount = 0 then
LFlag : = False
else
begin
LDStream.CopyFrom(LSStream,LCount);
I : = 0 ;
LTemp : = '' ;
end ;
end
else
LTemp : = LTemp + Char(LBuff);
end ;
finally
FreeAndNil(LSStream);
FreeAndNil(LDStream);
end ;
end ;
constructor TWnDownLoadThread.Create;
begin
inherited Create(True);
FreeOnTerminate : = True;
FTarget : = '' ;
FSaveFile : = '' ;
// New(FProxy);
end ;
procedure TWnDownLoadThread.DelIniFile;
begin
DeleteFile(FSaveLocation + FTempFile + S_INI_POSTFIX);
end ;
destructor TWnDownLoadThread.Destroy;
begin
// Dispose(FProxy);
inherited ;
end ;
procedure TWnDownLoadThread.DoBeginWork(Sender: TObject;
const APos,ACount: Int64);
begin
FFileSize : = ACount;
FCurrentFileSize : = APos ;
SaveIniFile;
Synchronize(DoUpdateUI);
end ;
procedure TWnDownLoadThread.DoEndWork(Sender: TObject);
begin
FCurrentFileSize : = FFileSize;
Synchronize(DoUpdateUI);
end ;
procedure TWnDownLoadThread.DoLog( const AMsg: string );
begin
if Assigned(FOnlog) then
begin
/// DoDownLoadLog(AMsg);
FWarningMsg: = AMsg;
Synchronize(DoSycLog);
end ;
end ;
procedure TWnDownLoadThread.DoSycLog;
begin
FOnlog(FWarningMsg);
end ;
procedure TWnDownLoadThread.DoUpdateUI;
begin
if Assigned(FProgress) then
FProgress(FCurrentFileSize,FFileSize);
end ;
procedure TWnDownLoadThread.DoWork(Sender: TObject; const AWork: Int64);
begin
FCurrentFileSize : = FCurrentFileSize + AWork;
Synchronize(DoUpdateUI);
end ;
procedure TWnDownLoadThread.Execute;
var
LDoc,
LHost,
LPath,
LProto,
LPort,
LBookmark: string ;
begin
if Length(FTarget) = 0 then Exit;
if Length(FSaveLocation) = 0 then Exit;
FHttp : = TCCHTTPClient.Create( nil );
try
FHttp.IOHandler : = TIdIOHandlerSocket.Create(FHttp);
FHttp.Socket.SocksInfo : = TIdSocksInfo.Create(FHttp);
FHttp.ProtocolVersion : = uCCHTTPClient.pv1_ 1 ;
SetHttpProxy;
FHttp.OnDownloadBegin : = DoBeginWork;
FHttp.OnDownload : = DoWork;
FHttp.OnDownloadEnd : = DoEndWork;
FHttp.OnLog : = DoLog;
FHttp.ParseURI(FTarget, LProto, LHost, LPath, LDoc, LPort, LBookmark);
FSaveFile : = LDoc;
FTempFile : = GetTempFile + ' . ' + LDoc;
if FReLoad and FileExists(FSaveLocation + FTempFile + S_TMP_POSTFIX) then
DeleteFile(FSaveLocation + FTempFile + S_TMP_POSTFIX);
DoLog( ' 下载开始请求 ' );
try
FHttp.DownLoad(FTarget,FSaveLocation + FTempFile + S_TMP_POSTFIX);
except
on E:Exception do
DoLog(e.Message);
end ;
if FileExists(FSaveLocation + FTempFile + S_INI_POSTFIX) then
begin
if FileExists(FSaveLocation + LDoc) then
DeleteFile(FSaveLocation + LDoc);
if FHttp.Response.TransferEncoding = ' chunked ' then
begin
chunkedConvertToFile(FSaveLocation + FTempFile + S_TMP_POSTFIX,FSaveLocation + LDoc);
DeleteFile(FSaveLocation + FTempFile + S_TMP_POSTFIX);
end
else
RenameFile(FSaveLocation + FTempFile + S_TMP_POSTFIX,FSaveLocation + LDoc);
DelIniFile;
end
else
DeleteFile(FSaveLocation + FTempFile + S_TMP_POSTFIX);
finally
FreeAndNil(FHttp);
DoLog( ' 下载结束 ' );
end ;
end ;
function TWnDownLoadThread.GetProxy: PWnProxySetting;
begin
Result : = @FProxy
end ;
function TWnDownLoadThread.GetTempFile: string ;
var
Ltemp: string ;
begin
Ltemp : = MD5Print(MD5String(FTarget));
// Ltemp : = copy(Ltemp, 2 , length(Ltemp) - 2 );
Result : = StringReplace(Ltemp, ' - ' , ' . ' ,[rfReplaceAll] );
end ;
procedure TWnDownLoadThread.SaveIniFile;
var
LIni:TIniFile;
begin
LIni : = TIniFile.Create(FSaveLocation + FTempFile + S_INI_POSTFIX);
try
LIni.WriteString( ' Setup ' , ' URL ' ,FTarget);
LIni.WriteString( ' Setup ' , ' SaveLocation ' ,FSaveLocation);
LIni.WriteString( ' Setup ' , ' SaveFile ' ,FSaveFile);
LIni.WriteString( ' Setup ' , ' TempFile ' ,FTempFile);
LIni.WriteString( ' Setup ' , ' FileSize ' ,FloatToStr(FFileSize));
finally
LIni.Free;
end ;
end ;
procedure TWnDownLoadThread.SetHttpProxy;
begin
if not Assigned(FHttp) then Exit;
with FProxy do
if FIsProxyEnabled then
begin
FHttp.Socket.SocksInfo.Version : = FSocksVersion;
FHttp.Socket.SocksInfo.Host : = FProxyHost;
FHttp.Socket.SocksInfo.Port : = FProxyPort;
if FHttp.Socket.SocksInfo.Username <> '' then
FHttp.Socket.SocksInfo.Authentication : = saUsernamePassword
else
FHttp.Socket.SocksInfo.Authentication : = saNoAuthentication;
FHttp.AuthUsername : = FAuthUserName;
FHttp.AuthPassword : = FAuthPassword;
FHttp.AuthDomain : = FAuthDomain;
FHttp.ProxyHost : = FProxyHost;
FHttp.ProxyPort : = FProxyPort;
end ;
end ;
procedure TWnDownLoadThread.SetOnLog( const Value: TDolog);
begin
FOnLog : = Value;
end ;
procedure TWnDownLoadThread.SetProgress( const Value: TWnDownloadProgress);
begin
FProgress : = Value;
end ;
procedure TWnDownLoadThread.SetProxy( const Value: PWnProxySetting);
begin
// New(FProxy);
FProxy.FIsProxyEnabled : = Value^.FIsProxyEnabled;
FProxy.FSocksVersion : = Value^.FSocksVersion;
FProxy.FProxyHost : = Value^.FProxyHost;
FProxy.FProxyPort : = Value^.FProxyPort;
FProxy.FAuthUserName : = Value^.FAuthUserName;
FProxy.FAuthPassword : = Value^.FAuthPassword;
FProxy.FAuthDomain : = Value^.FAuthDomain;
FProxy.FProxyHost : = Value^.FProxyHost;
FProxy.FProxyPort : = Value^.FProxyPort;
end ;
procedure TWnDownLoadThread.SetReLoad( const Value: Boolean);
begin
FReLoad : = Value;
end ;
procedure TWnDownLoadThread.SetSaveLocation( const Value: string );
begin
FSaveLocation : = Value;
if FSaveLocation[Length(FSaveLocation)] <> ' \ ' then
FSaveLocation : = FSaveLocation + ' \ ' ;
end ;
procedure TWnDownLoadThread.SetTarget( const Value: string );
begin
if Length(Value) = 0 then Exit;
if UpperCase(Copy(Value, 1 , 7 )) <> ' HTTP:// ' then Exit;
FTarget : = Value;
end ;
end .
interface
uses
IdHTTP, IdAuthentication, IdHeaderList, IniFiles, md5,
SysUtils, IdAuthenticationSSPI, IdAuthenticationDigest, Classes, IdException,
IdSocks, IdIOHandlerSocket, DateUtils, IdComponent, Forms,ucchttpClient;
const
S_TMP_POSTFIX = ' .tmp ' ;
S_INI_POSTFIX = ' .sni ' ;
type
TWnDownloadProgress = procedure (APosition,ACount: Int64) of object ;
// 代理设置结构
TWnProxySetting = packed record
FIsProxyEnabled: Boolean;
FSocksVersion: TSocksVersion;
FProxyHost: string ;
FProxyPort: Word;
FAuthUserName: string ;
FAuthPassword: string ;
FAuthDomain: string ;
end ;
PWnProxySetting = ^TWnProxySetting;
TWnDownLoadThread = class (TThread)
private
FHttp: TCCHTTPClient;
FProxy: TWnProxySetting;
FTarget: string ;
FSaveFile: string ;
FTempFile: string ;
FProgress: TWnDownloadProgress;
FCurrentFileSize,FFileSize : Int64;
FSaveLocation: string ;
FOnLog: TDolog;
FWarningMsg: string ;
FReLoad: Boolean;
procedure SetProxy( const Value: PWnProxySetting);
function GetProxy :PWnProxySetting;
procedure SetHttpProxy;
procedure SetTarget( const Value: string );
procedure SetProgress( const Value: TWnDownloadProgress);
procedure DoBeginWork(Sender: TObject; const APos,ACount: Int64);
procedure DoWork(Sender: TObject; const AWork:Int64);
procedure DoEndWork(Sender: TObject);
procedure DoUpdateUI;
procedure SetSaveLocation( const Value: string );
procedure SaveIniFile;
procedure DelIniFile;
procedure DoLog( const AMsg: string );
procedure DoSycLog;
function GetTempFile: string ;
procedure SetOnLog( const Value: TDolog);
procedure SetReLoad( const Value: Boolean);
public
procedure Execute; override ;
constructor Create;
destructor Destroy; override ;
class procedure chunkedConvertToFile( const ASource,ADestination: string );
property PProxy:PWnProxySetting read GetProxy write SetProxy;
property Target: string read FTarget write SetTarget;
property SaveLocation : string read FSaveLocation write SetSaveLocation;
property Progress:TWnDownloadProgress read FProgress write SetProgress;
property ReLoad:Boolean read FReLoad write SetReLoad;
property OnLog: TDolog read FOnLog write SetOnLog;
end ;
procedure DownLoadFile( const AUrl,ASaveFile: string ;AProxy:PWnProxySetting;AProgress:TWnDownloadProgress;ALog:TDolog;AReLoad:Boolean);
implementation
procedure DownLoadFile( const AUrl,ASaveFile: string ;AProxy:PWnProxySetting;AProgress:TWnDownloadProgress;ALog:TDolog;AReLoad:Boolean);
var
LThread: TWnDownLoadThread;
begin
LThread : = TWnDownLoadThread.Create;
LThread.ReLoad : = AReLoad;
LThread.Target : = AUrl;
LThread.SaveLocation : = ASaveFile;
LThread.PProxy : = AProxy;
LThread.Progress : = AProgress;
LThread.OnLog : = ALog;
LThread.Resume;
end ;
{ TWnDownLoadThread }
class procedure TWnDownLoadThread.chunkedConvertToFile( const ASource,
ADestination: string );
var
LSStream,LDStream: TFileStream;
LBuff: Byte;
LFlag: Boolean;
LTemp : string ;
I,LCount : Integer;
begin
if not FileExists(ASource) then Exit;
if FileExists(ADestination) then Exit;
LSStream : = TFileStream.Create(ASource,fmOpenRead );
LDStream : = TFileStream.Create(ADestination,fmCreate);
try
LSStream.Seek( 0 , 0 );
LFlag : = True;
I : = 0 ;
while LFlag do
begin
LSStream.Read(LBuff,SizeOf(Byte));
Inc(I);
if (LBuff = $0A) and (I > 2 ) and (LTemp[I - 1 ] = #$0D) then begin
LCount : = StrToIntDef( ' $ ' + trim(LTemp), 1 );
if LCount = 1 then
begin
LCount : = 0 ;
Break;
// raise 异常
end ;
if LCount = 0 then
LFlag : = False
else
begin
LDStream.CopyFrom(LSStream,LCount);
I : = 0 ;
LTemp : = '' ;
end ;
end
else
LTemp : = LTemp + Char(LBuff);
end ;
finally
FreeAndNil(LSStream);
FreeAndNil(LDStream);
end ;
end ;
constructor TWnDownLoadThread.Create;
begin
inherited Create(True);
FreeOnTerminate : = True;
FTarget : = '' ;
FSaveFile : = '' ;
// New(FProxy);
end ;
procedure TWnDownLoadThread.DelIniFile;
begin
DeleteFile(FSaveLocation + FTempFile + S_INI_POSTFIX);
end ;
destructor TWnDownLoadThread.Destroy;
begin
// Dispose(FProxy);
inherited ;
end ;
procedure TWnDownLoadThread.DoBeginWork(Sender: TObject;
const APos,ACount: Int64);
begin
FFileSize : = ACount;
FCurrentFileSize : = APos ;
SaveIniFile;
Synchronize(DoUpdateUI);
end ;
procedure TWnDownLoadThread.DoEndWork(Sender: TObject);
begin
FCurrentFileSize : = FFileSize;
Synchronize(DoUpdateUI);
end ;
procedure TWnDownLoadThread.DoLog( const AMsg: string );
begin
if Assigned(FOnlog) then
begin
/// DoDownLoadLog(AMsg);
FWarningMsg: = AMsg;
Synchronize(DoSycLog);
end ;
end ;
procedure TWnDownLoadThread.DoSycLog;
begin
FOnlog(FWarningMsg);
end ;
procedure TWnDownLoadThread.DoUpdateUI;
begin
if Assigned(FProgress) then
FProgress(FCurrentFileSize,FFileSize);
end ;
procedure TWnDownLoadThread.DoWork(Sender: TObject; const AWork: Int64);
begin
FCurrentFileSize : = FCurrentFileSize + AWork;
Synchronize(DoUpdateUI);
end ;
procedure TWnDownLoadThread.Execute;
var
LDoc,
LHost,
LPath,
LProto,
LPort,
LBookmark: string ;
begin
if Length(FTarget) = 0 then Exit;
if Length(FSaveLocation) = 0 then Exit;
FHttp : = TCCHTTPClient.Create( nil );
try
FHttp.IOHandler : = TIdIOHandlerSocket.Create(FHttp);
FHttp.Socket.SocksInfo : = TIdSocksInfo.Create(FHttp);
FHttp.ProtocolVersion : = uCCHTTPClient.pv1_ 1 ;
SetHttpProxy;
FHttp.OnDownloadBegin : = DoBeginWork;
FHttp.OnDownload : = DoWork;
FHttp.OnDownloadEnd : = DoEndWork;
FHttp.OnLog : = DoLog;
FHttp.ParseURI(FTarget, LProto, LHost, LPath, LDoc, LPort, LBookmark);
FSaveFile : = LDoc;
FTempFile : = GetTempFile + ' . ' + LDoc;
if FReLoad and FileExists(FSaveLocation + FTempFile + S_TMP_POSTFIX) then
DeleteFile(FSaveLocation + FTempFile + S_TMP_POSTFIX);
DoLog( ' 下载开始请求 ' );
try
FHttp.DownLoad(FTarget,FSaveLocation + FTempFile + S_TMP_POSTFIX);
except
on E:Exception do
DoLog(e.Message);
end ;
if FileExists(FSaveLocation + FTempFile + S_INI_POSTFIX) then
begin
if FileExists(FSaveLocation + LDoc) then
DeleteFile(FSaveLocation + LDoc);
if FHttp.Response.TransferEncoding = ' chunked ' then
begin
chunkedConvertToFile(FSaveLocation + FTempFile + S_TMP_POSTFIX,FSaveLocation + LDoc);
DeleteFile(FSaveLocation + FTempFile + S_TMP_POSTFIX);
end
else
RenameFile(FSaveLocation + FTempFile + S_TMP_POSTFIX,FSaveLocation + LDoc);
DelIniFile;
end
else
DeleteFile(FSaveLocation + FTempFile + S_TMP_POSTFIX);
finally
FreeAndNil(FHttp);
DoLog( ' 下载结束 ' );
end ;
end ;
function TWnDownLoadThread.GetProxy: PWnProxySetting;
begin
Result : = @FProxy
end ;
function TWnDownLoadThread.GetTempFile: string ;
var
Ltemp: string ;
begin
Ltemp : = MD5Print(MD5String(FTarget));
// Ltemp : = copy(Ltemp, 2 , length(Ltemp) - 2 );
Result : = StringReplace(Ltemp, ' - ' , ' . ' ,[rfReplaceAll] );
end ;
procedure TWnDownLoadThread.SaveIniFile;
var
LIni:TIniFile;
begin
LIni : = TIniFile.Create(FSaveLocation + FTempFile + S_INI_POSTFIX);
try
LIni.WriteString( ' Setup ' , ' URL ' ,FTarget);
LIni.WriteString( ' Setup ' , ' SaveLocation ' ,FSaveLocation);
LIni.WriteString( ' Setup ' , ' SaveFile ' ,FSaveFile);
LIni.WriteString( ' Setup ' , ' TempFile ' ,FTempFile);
LIni.WriteString( ' Setup ' , ' FileSize ' ,FloatToStr(FFileSize));
finally
LIni.Free;
end ;
end ;
procedure TWnDownLoadThread.SetHttpProxy;
begin
if not Assigned(FHttp) then Exit;
with FProxy do
if FIsProxyEnabled then
begin
FHttp.Socket.SocksInfo.Version : = FSocksVersion;
FHttp.Socket.SocksInfo.Host : = FProxyHost;
FHttp.Socket.SocksInfo.Port : = FProxyPort;
if FHttp.Socket.SocksInfo.Username <> '' then
FHttp.Socket.SocksInfo.Authentication : = saUsernamePassword
else
FHttp.Socket.SocksInfo.Authentication : = saNoAuthentication;
FHttp.AuthUsername : = FAuthUserName;
FHttp.AuthPassword : = FAuthPassword;
FHttp.AuthDomain : = FAuthDomain;
FHttp.ProxyHost : = FProxyHost;
FHttp.ProxyPort : = FProxyPort;
end ;
end ;
procedure TWnDownLoadThread.SetOnLog( const Value: TDolog);
begin
FOnLog : = Value;
end ;
procedure TWnDownLoadThread.SetProgress( const Value: TWnDownloadProgress);
begin
FProgress : = Value;
end ;
procedure TWnDownLoadThread.SetProxy( const Value: PWnProxySetting);
begin
// New(FProxy);
FProxy.FIsProxyEnabled : = Value^.FIsProxyEnabled;
FProxy.FSocksVersion : = Value^.FSocksVersion;
FProxy.FProxyHost : = Value^.FProxyHost;
FProxy.FProxyPort : = Value^.FProxyPort;
FProxy.FAuthUserName : = Value^.FAuthUserName;
FProxy.FAuthPassword : = Value^.FAuthPassword;
FProxy.FAuthDomain : = Value^.FAuthDomain;
FProxy.FProxyHost : = Value^.FProxyHost;
FProxy.FProxyPort : = Value^.FProxyPort;
end ;
procedure TWnDownLoadThread.SetReLoad( const Value: Boolean);
begin
FReLoad : = Value;
end ;
procedure TWnDownLoadThread.SetSaveLocation( const Value: string );
begin
FSaveLocation : = Value;
if FSaveLocation[Length(FSaveLocation)] <> ' \ ' then
FSaveLocation : = FSaveLocation + ' \ ' ;
end ;
procedure TWnDownLoadThread.SetTarget( const Value: string );
begin
if Length(Value) = 0 then Exit;
if UpperCase(Copy(Value, 1 , 7 )) <> ' HTTP:// ' then Exit;
FTarget : = Value;
end ;
end .