如何使用 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())。