gusucode.com > 一个视频压缩并通过网络来传送的源代码 > 一个视频压缩并通过网络来传送的源代码/断点续传和多线程VC源码/code/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);
}
//---------------------------------------------------------------------------