如何使用 TIdHTTP 一次下载多个文件
How to download multiple files at once with TIdHTTP
我目前正在使用来自 Indy 的 TIdHTTP
和我的 Embarcadero C++Builder 10.1 Berlin。我已经阅读了一些关于如何使 TIdHTTP
成为多线程的在线教程,但主要问题是我已经在一个线程中测试了这个组件。
这就是它的工作原理。我创建了一个线程对象,并创建了一个函数来在该线程中下载文件,显然该线程工作正常并且文件已下载到我的磁盘。但是,当我为文件下载创建额外的线程时,第一个线程停止了。我不想这样,我希望两个文件同时继续下载(不暂停第一个线程),就像在 IDM(Internet 下载管理器)中一样。
线程 class 就像下面的代码:
class TIdHTTPThread : public TThread
{
protected:
void __fastcall Execute();
void __fastcall PutDownloadedFile();
public:
__fastcall TIdHTTPThread(bool CreateSuspended);
void __fastcall IdHTTPBeginWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCountMax);
void __fastcall IdHTTPWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCount);
void __fastcall IdHTTPEndWork(TObject *ASender, TWorkMode AWorkMode);
void __fastcall DownloadFile(UnicodeString AFileURL, UnicodeString AFileDest);
void __fastcall CreateQueue(TWinControl* wcParent, TAlign alAlign);
private:
TIdHTTP* IdHTTP;
TMemoryStream* msMemoryStream;
UnicodeString uFileURL;
UnicodeString uFileDest;
int iDownProgress;
int iFileSize;
int iMaxProgress;
int iDownSpeed;
TWinControl* wcParent;
TIFDQueue *ifdQueue;
};
请不要理会class中的额外属性和方法,我只想实现我的问题。
CPP 文件:
void __fastcall TIdHTTPThread::CreateQueue(TWinControl* wcParent, TAlign alAlign)
{
this->wcParent = wcParent;
ifdQueue = new TIFDQueue(this->wcParent, alAlign);
}
void __fastcall TIdHTTPThread::IdHTTPBeginWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCountMax)
{
this->iFileSize = AWorkCountMax;
this->iMaxProgress = AWorkCountMax;
ifdQueue->SetFileSize(this->iFileSize);
ifdQueue->SetMaxProgress(this->iMaxProgress);
ifdQueue->SetFileURL(this->uFileURL);
ifdQueue->SetFilePath(this->uFileDest);
ifdQueue->OnBeginUpdate();
}
void __fastcall TIdHTTPThread::IdHTTPWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCount)
{
this->iDownProgress = AWorkCount;
this->iDownSpeed = AWorkCount / 1024;
ifdQueue->SetDownProgress(this->iDownProgress);
ifdQueue->SetDownSpeed(this->iDownSpeed);
ifdQueue->OnWorkUpdate();
}
void __fastcall TIdHTTPThread::IdHTTPEndWork(TObject *ASender, TWorkMode AWorkMode)
{
ifdQueue->OnEndUpdate();
this->Terminate();
}
//**//
void __fastcall TIdHTTPThread::DownloadFile(UnicodeString AFileURL, UnicodeString AFileDest)
{
this->uFileURL = AFileURL;
this->uFileDest = AFileDest;
}
void __fastcall TIdHTTPThread::PutDownloadedFile()
{
try {
this->msMemoryStream = new TMemoryStream;
this->IdHTTP = new TIdHTTP(NULL);
this->IdHTTP->OnWorkBegin = this->IdHTTPBeginWork;
this->IdHTTP->OnWork = this->IdHTTPWork;
this->IdHTTP->OnWorkEnd = this->IdHTTPEndWork;
this->IdHTTP->ConnectTimeout = 20000;
this->IdHTTP->ReadTimeout = 60000;
this->IdHTTP->Get(this->uFileURL, this->msMemoryStream);
this->msMemoryStream->SaveToFile(this->uFileDest);
} __finally {
delete this->msMemoryStream;
delete this->IdHTTP;
}
}
__fastcall TIdHTTPThread::TIdHTTPThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TIdHTTPThread::Execute()
{
//---- Place thread code here ----
FreeOnTerminate = true;
Synchronize(&this->PutDownloadedFile);
}
//---------------------------------------------------------------------------
更新:
void __fastcall TIdHTTPThread::PutDownloadedFile()
{
try {
this->CookieManager = new TIdCookieManager(NULL);
this->SSLIOHandlerSocket = new TIdSSLIOHandlerSocketOpenSSL(NULL);
this->msMemoryStream = new TMemoryStream;
// Configure SSL IOHandler
this->SSLIOHandlerSocket->SSLOptions->Method = sslvSSLv23;
this->SSLIOHandlerSocket->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1_2 << sslvTLSv1_1 << sslvTLSv1;
// Setup HTTP
this->IdHTTP = new TIdHTTP(NULL);
this->ifdQueue->StopDownload(this->IdHTTP); // Function To stop download When Fired (doesn't fire imidiatly)
this->IdHTTP->OnWorkBegin = this->IdHTTPBeginWork;
this->IdHTTP->OnWork = this->IdHTTPWork;
this->IdHTTP->OnWorkEnd = this->IdHTTPEndWork;
this->IdHTTP->OnRedirect = this->IdHTTPRedirect;
this->IdHTTP->HandleRedirects = true;
this->IdHTTP->AllowCookies = true;
this->IdHTTP->CookieManager = this->CookieManager;
this->IdHTTP->IOHandler = this->SSLIOHandlerSocket;
this->IdHTTP->Get(this->uFileURL, this->msMemoryStream);
if ( this->msMemoryStream->Size >= this->IdHTTP->Response->ContentLength ) {
this->msMemoryStream->SaveToFile(this->uFileName);
}
} __finally {
delete this->msMemoryStream;
delete this->CookieManager;
delete this->SSLIOHandlerSocket;
delete this->IdHTTP;
}
}
问题是您的线程的 Execute()
方法正在 Synchronize()
调用中完成其所有工作,因此它的所有工作实际上都在主 UI 线程中完成,从而序列化您的下载并破坏使用工作线程的全部意义。
不要用 Synchronize()
调用 PutDownloadedFile()
本身。在更新 UI 控件时,您需要更改个人 TIdHTTP
状态事件以使用 Synchronize()
(或 Queue()
)。
我目前正在使用来自 Indy 的 TIdHTTP
和我的 Embarcadero C++Builder 10.1 Berlin。我已经阅读了一些关于如何使 TIdHTTP
成为多线程的在线教程,但主要问题是我已经在一个线程中测试了这个组件。
这就是它的工作原理。我创建了一个线程对象,并创建了一个函数来在该线程中下载文件,显然该线程工作正常并且文件已下载到我的磁盘。但是,当我为文件下载创建额外的线程时,第一个线程停止了。我不想这样,我希望两个文件同时继续下载(不暂停第一个线程),就像在 IDM(Internet 下载管理器)中一样。
线程 class 就像下面的代码:
class TIdHTTPThread : public TThread
{
protected:
void __fastcall Execute();
void __fastcall PutDownloadedFile();
public:
__fastcall TIdHTTPThread(bool CreateSuspended);
void __fastcall IdHTTPBeginWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCountMax);
void __fastcall IdHTTPWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCount);
void __fastcall IdHTTPEndWork(TObject *ASender, TWorkMode AWorkMode);
void __fastcall DownloadFile(UnicodeString AFileURL, UnicodeString AFileDest);
void __fastcall CreateQueue(TWinControl* wcParent, TAlign alAlign);
private:
TIdHTTP* IdHTTP;
TMemoryStream* msMemoryStream;
UnicodeString uFileURL;
UnicodeString uFileDest;
int iDownProgress;
int iFileSize;
int iMaxProgress;
int iDownSpeed;
TWinControl* wcParent;
TIFDQueue *ifdQueue;
};
请不要理会class中的额外属性和方法,我只想实现我的问题。
CPP 文件:
void __fastcall TIdHTTPThread::CreateQueue(TWinControl* wcParent, TAlign alAlign)
{
this->wcParent = wcParent;
ifdQueue = new TIFDQueue(this->wcParent, alAlign);
}
void __fastcall TIdHTTPThread::IdHTTPBeginWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCountMax)
{
this->iFileSize = AWorkCountMax;
this->iMaxProgress = AWorkCountMax;
ifdQueue->SetFileSize(this->iFileSize);
ifdQueue->SetMaxProgress(this->iMaxProgress);
ifdQueue->SetFileURL(this->uFileURL);
ifdQueue->SetFilePath(this->uFileDest);
ifdQueue->OnBeginUpdate();
}
void __fastcall TIdHTTPThread::IdHTTPWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCount)
{
this->iDownProgress = AWorkCount;
this->iDownSpeed = AWorkCount / 1024;
ifdQueue->SetDownProgress(this->iDownProgress);
ifdQueue->SetDownSpeed(this->iDownSpeed);
ifdQueue->OnWorkUpdate();
}
void __fastcall TIdHTTPThread::IdHTTPEndWork(TObject *ASender, TWorkMode AWorkMode)
{
ifdQueue->OnEndUpdate();
this->Terminate();
}
//**//
void __fastcall TIdHTTPThread::DownloadFile(UnicodeString AFileURL, UnicodeString AFileDest)
{
this->uFileURL = AFileURL;
this->uFileDest = AFileDest;
}
void __fastcall TIdHTTPThread::PutDownloadedFile()
{
try {
this->msMemoryStream = new TMemoryStream;
this->IdHTTP = new TIdHTTP(NULL);
this->IdHTTP->OnWorkBegin = this->IdHTTPBeginWork;
this->IdHTTP->OnWork = this->IdHTTPWork;
this->IdHTTP->OnWorkEnd = this->IdHTTPEndWork;
this->IdHTTP->ConnectTimeout = 20000;
this->IdHTTP->ReadTimeout = 60000;
this->IdHTTP->Get(this->uFileURL, this->msMemoryStream);
this->msMemoryStream->SaveToFile(this->uFileDest);
} __finally {
delete this->msMemoryStream;
delete this->IdHTTP;
}
}
__fastcall TIdHTTPThread::TIdHTTPThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TIdHTTPThread::Execute()
{
//---- Place thread code here ----
FreeOnTerminate = true;
Synchronize(&this->PutDownloadedFile);
}
//---------------------------------------------------------------------------
更新:
void __fastcall TIdHTTPThread::PutDownloadedFile()
{
try {
this->CookieManager = new TIdCookieManager(NULL);
this->SSLIOHandlerSocket = new TIdSSLIOHandlerSocketOpenSSL(NULL);
this->msMemoryStream = new TMemoryStream;
// Configure SSL IOHandler
this->SSLIOHandlerSocket->SSLOptions->Method = sslvSSLv23;
this->SSLIOHandlerSocket->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1_2 << sslvTLSv1_1 << sslvTLSv1;
// Setup HTTP
this->IdHTTP = new TIdHTTP(NULL);
this->ifdQueue->StopDownload(this->IdHTTP); // Function To stop download When Fired (doesn't fire imidiatly)
this->IdHTTP->OnWorkBegin = this->IdHTTPBeginWork;
this->IdHTTP->OnWork = this->IdHTTPWork;
this->IdHTTP->OnWorkEnd = this->IdHTTPEndWork;
this->IdHTTP->OnRedirect = this->IdHTTPRedirect;
this->IdHTTP->HandleRedirects = true;
this->IdHTTP->AllowCookies = true;
this->IdHTTP->CookieManager = this->CookieManager;
this->IdHTTP->IOHandler = this->SSLIOHandlerSocket;
this->IdHTTP->Get(this->uFileURL, this->msMemoryStream);
if ( this->msMemoryStream->Size >= this->IdHTTP->Response->ContentLength ) {
this->msMemoryStream->SaveToFile(this->uFileName);
}
} __finally {
delete this->msMemoryStream;
delete this->CookieManager;
delete this->SSLIOHandlerSocket;
delete this->IdHTTP;
}
}
问题是您的线程的 Execute()
方法正在 Synchronize()
调用中完成其所有工作,因此它的所有工作实际上都在主 UI 线程中完成,从而序列化您的下载并破坏使用工作线程的全部意义。
不要用 Synchronize()
调用 PutDownloadedFile()
本身。在更新 UI 控件时,您需要更改个人 TIdHTTP
状态事件以使用 Synchronize()
(或 Queue()
)。