📄 thttpgetex.cpp
字号:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "THttpGetEx.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
AnsiString GetSystemTemp(void)
{
AnsiString T;
char Tmp[1024];
if(GetTempPath(1024,Tmp)>0)T=Tmp;
return T;
}
//---------------------------------------------------------------------------
__fastcall THttpGetEx::THttpGetEx(TComponent* Owner)
: TComponent(Owner)
{
FHttpThreadCount=3;
FHttpThreadCreated=false;
FWorking=false;
FFromBreakpoint=false;
}
//---------------------------------------------------------------------------
__fastcall THttpGetEx::~THttpGetEx()
{
delete []HttpThreads;
}
//---------------------------------------------------------------------------
namespace Thttpgetex
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(THttpGetEx)};
RegisterComponents("Samples", classes, 0);
}
}
//---------------------------------------------------------------------------
// 分配资源
void THttpGetEx::AssignResource(void)
{
FSuccesss=new bool[FHttpThreadCount];
for(int i=0;i<FHttpThreadCount;i++)
{
FSuccesss[i]=false;
}
// 输出文件名称
OutTmpFiles = new AnsiString[FHttpThreadCount];
AnsiString ShortName=ExtractFileName(FOutFileName);
AnsiString Path=GetSystemTemp();
for(int i=0;i<FHttpThreadCount;i++)
{
OutTmpFiles[i]=Path+ShortName+"-"+IntToStr(i)+".hpt";
}
// 建立线程
HttpThreads = new THttpGetThread *[FHttpThreadCount];
}
//---------------------------------------------------------------------------
// 释放资源
void THttpGetEx::ReleaseResource(void)
{
delete HttpThreads;
delete OutTmpFiles;
delete FSuccesss;
HttpThreads=NULL;
OutTmpFiles=NULL;
FSuccesss=NULL;
FHttpThreadCreated=false;
}
//---------------------------------------------------------------------------
// 创建一个下载线程
THttpGetThread * THttpGetEx::CreateHttpThread(void)
{
THttpGetThread *HttpThread=new THttpGetThread(this);
HttpThread->FromBreakpoint=FFromBreakpoint;
HttpThread->URL=FURL;
HttpThread->OnAbort=OnThreadError;
HttpThread->OnError=OnThreadError;
HttpThread->OnComplete=OnThreadComplete;
HttpThread->OnGetFileSize=FOnGetFileSize;
HttpThread->OnProgress=FOnProgress;
HttpThread->OnStatusText=FOnStatusText;
return HttpThread;
}
//---------------------------------------------------------------------------
// 创建下载线程
void THttpGetEx::CreateHttpThreads(void)
{
if(FHttpThreadCreated)return;
FHttpThreadCreated=true;
AssignResource();
// 取得文件大小,以决定各个线程下载的起始位置
THttpGetThread *HttpThread=CreateHttpThread();
HttpThreads[FHttpThreadCount-1]=HttpThread;
int FileSize=HttpThread->GetWebFileSize();
// 把文件分成FHttpThreadCount块
int AvgSize=FileSize/FHttpThreadCount;
int *Starts= new int[FHttpThreadCount];
int *Bytes = new int[FHttpThreadCount];
for(int i=0;i<FHttpThreadCount;i++)
{
Starts[i]=i*AvgSize;
Bytes[i] =AvgSize;
}
// 修正最后一块的大小
Bytes[FHttpThreadCount-1]=AvgSize+(FileSize-AvgSize*FHttpThreadCount);
// 检查服务器是否支持断点续传
HttpThread->StartPostion=Starts[FHttpThreadCount-1];
HttpThread->GetBytes=Bytes[FHttpThreadCount-1];
bool CanMulti=HttpThread->SetFilePointer();
if(CanMulti==false) // 不支持,直接下载
{
FHttpThreadCount=1;
HttpThread->StartPostion=0;
HttpThread->GetBytes=FileSize;
HttpThread->Index=0;
HttpThread->OutFileName=OutTmpFiles[0];
}else
{
HttpThread->OutFileName=OutTmpFiles[FHttpThreadCount-1];
HttpThread->Index=FHttpThreadCount-1;
// 支持断点续传,建立多个线程
for(int i=0;i<FHttpThreadCount-1;i++)
{
HttpThread=CreateHttpThread();
HttpThread->StartPostion=Starts[i];
HttpThread->GetBytes=Bytes[i];
HttpThread->OutFileName=OutTmpFiles[i];
HttpThread->Index=i;
HttpThreads[i]=HttpThread;
}
}
// 删除临时变量
delete Starts;
delete Bytes;
}
//---------------------------------------------------------------------------
// 开始下载
void __fastcall THttpGetEx::StartGet(void)
{
if(FURL.IsEmpty() || FOutFileName.IsEmpty())return;
CreateHttpThreads();
THttpGetThread *HttpThread;
for(int i=0;i<FHttpThreadCount;i++)
{
HttpThread=HttpThreads[i];
if(HttpThread->Suspended)HttpThread->Resume();
}
}
//---------------------------------------------------------------------------
// 重新开始下载
void __fastcall THttpGetEx::RestartGet(void)
{
if(FURL.IsEmpty() || FOutFileName.IsEmpty())return;
Stop();
StartGet();
}
//---------------------------------------------------------------------------
// 暂停下载
void __fastcall THttpGetEx::Pause(void)
{
if(FWorking==false)return;
// 请自己实现
}
//---------------------------------------------------------------------------
// 停止下载
void __fastcall THttpGetEx::Stop(void)
{
if(FWorking==false)return;
// 请自己实现
ReleaseResource();
}
//---------------------------------------------------------------------------
const char *MutexToThread="http-get-thread-mutex";
void __fastcall THttpGetEx::OnThreadComplete(TObject *Sender, int Index)
{
// 创建互斥对象
HANDLE hMutex= CreateMutex(NULL,FALSE,MutexToThread);
DWORD Err=GetLastError();
if(Err==ERROR_ALREADY_EXISTS) // 已经存在,等待
{
WaitForSingleObject(hMutex,INFINITE);//8000L);
hMutex= CreateMutex(NULL,FALSE,MutexToThread);
}
// 当一个线程结束时,检查是否全部认为完成
FSuccesss[Index]=true;
bool S=true;
for(int i=0;i<FHttpThreadCount;i++)
{
S = S && FSuccesss[i];
}
ReleaseMutex(hMutex);
if(S)// 下载完成
{
DoOnComplete();
}
}
//---------------------------------------------------------------------------
void __fastcall THttpGetEx::OnThreadError(TObject *Sender, int Index)
{
// 当一个线程结束时,检查
Stop();
DoOnError();
}
//---------------------------------------------------------------------------
void THttpGetEx::DoOnComplete(void)
{
if(FOnComplete)
{
DoOnStatusText("download complete");
// 合并各个文件
DoOnStatusText("merge all parts");
// 1. 复制第一部分
CopyFile(OutTmpFiles[0].c_str(),FOutFileName.c_str(),false);
// 添加其他部分
int hD=FileOpen(FOutFileName,fmOpenWrite);
FileSeek(hD,0,2); // 移动文件指针到末尾
if(hD==-1)
{
DoOnError();
return;
}
const int BufSize=1024*4;
char Buf[BufSize+4];
int Reads;
for(int i=1;i<FHttpThreadCount;i++)
{
int hS=FileOpen(OutTmpFiles[i],fmOpenRead);
// 复制数据
Reads=FileRead(hS,(void *)Buf,BufSize);
while(Reads>0)
{
FileWrite(hD,(void *)Buf,Reads);
Reads=FileRead(hS,(void *)Buf,BufSize);
}
FileClose(hS);
}
FileClose(hD);
DoOnStatusText("all complete");
FOnComplete(this);
}
}
//---------------------------------------------------------------------------
void THttpGetEx::DoOnError(void)
{
if(FOnError)
FOnError(this);
}
//---------------------------------------------------------------------------
void THttpGetEx::DoOnStatusText(AnsiString Text)
{
if(FOnStatusText)
FOnStatusText(this,Text,-1);
}
//---------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -