⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 downloadmtr.cpp

📁 多线程下载的
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// DownloadMTR.cpp: implementation of the CDownloadMTR class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "NetDownMTR.h"
#include "DownloadMTR.h"
#include <io.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

void DownloadNotify ( int nIndex, UINT nNotityType, LPVOID lpNotifyData, LPVOID pDownloadMTR );

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CDownloadMTR::CDownloadMTR()
	: m_nThreadCount ( DEFAULT_THREAD_COUNT )
	, m_pDownloadPub_MTR ( NULL )
	, m_pDownloadPub_Info ( NULL )
	, m_pDownloadCellInfo ( NULL )
	, m_hThread ( NULL )
	, m_bForceDownload ( FALSE )
	, m_nTotalDownloadedSize_ThisTimes ( 0 )
{
	memset ( &m_BaseDownInfo, 0, sizeof(t_BaseDownInfo) );
	m_hEvtEndModule = ::CreateEvent ( NULL, TRUE, FALSE, NULL );
	m_dwDownloadStartTime = GetTickCount();
}

CDownloadMTR::~CDownloadMTR()
{
	StopDownload ();
}

//
// 设置下载的线程数
//
BOOL CDownloadMTR::SetThreadCount(int nThreadCount)
{
	if ( nThreadCount <= 0 || nThreadCount > MAX_DOWNLOAD_THREAD_COUNT )
	{
		Log ( L_WARNING, "Thread count %d is invalid. Rang [%d-%d]", nThreadCount, 1, MAX_DOWNLOAD_THREAD_COUNT );
		return FALSE;
	}
	if ( nThreadCount == m_nThreadCount )
		return TRUE;

	m_nThreadCount = nThreadCount;
	return TRUE;
}

//
// 下载任务的线程函数
//
DWORD WINAPI ThreadProc_DownloadMTR(
  LPVOID lpParameter   // thread data
)
{
	CDownloadMTR *pDownloadMTR = (CDownloadMTR*)lpParameter;
	ASSERT ( pDownloadMTR );
	pDownloadMTR->ThreadProc_DownloadMTR ();
	TRACE ( "下载任务的线程函数 执行完毕\n" );
	return TRUE;
}

BOOL CDownloadMTR::ThreadProc_DownloadMTR()
{
	// 启动多线程下载任务
	int nRet = StartMTRDownload ();
	if ( nRet == 2 ) return HandleDownloadFinished(ENUM_DOWNLOAD_RESULT_SUCCESS);
	if ( nRet == 0 ) return HandleDownloadFinished(ENUM_DOWNLOAD_RESULT_FAILED);

	// 等待所有线程下载完成
	ENUM_DOWNLOAD_RESULT eDownloadResult = WaitForDownloadFinished ();
	if ( eDownloadResult == ENUM_DOWNLOAD_RESULT_SUCCESS && !GetDownloadResult () )
	{
		eDownloadResult = ENUM_DOWNLOAD_RESULT_FAILED;
	}

	return HandleDownloadFinished ( eDownloadResult );
}

//
// 多线程断点续传下载一个文件
//
BOOL CDownloadMTR::Download (
		LPCTSTR lpszDownloadURL,
		LPCTSTR lpszSavePath,
		LPCTSTR lpszSaveOnlyFileName,
		LPCTSTR lpszUsername/*=NULL*/,
		LPCTSTR lpszPassword/*=NULL*/,
		BOOL bForceDownload/*=FALSE*/		// 如果为 TRUE 表示强制性重新下载,以下载的部分将会被删除,FALSE 表示断点续传
	)
{
	if ( !HANDLE_IS_VALID(m_hEvtEndModule) )
		return FALSE;
	if ( !lpszSavePath || strlen(lpszSavePath) < 1 )
		return FALSE;
	m_csSavePath = lpszSavePath;
	m_csSaveOnlyFileName = GET_SAFE_STRING(lpszSaveOnlyFileName);
	m_bForceDownload = bForceDownload;

	CString csServer, csObject;
	USHORT nPort = 0;
	if ( !ParseURL ( lpszDownloadURL, csServer, csObject, nPort, m_csProtocolType ) )
	{
		Log ( L_ERROR, "Download URL [%s] invalid", lpszDownloadURL );
		return FALSE;
	}
	m_csDownloadURL = lpszDownloadURL;

	// 创建取站点信息对象
	if ( !( m_pDownloadPub_Info = CreateDownloadObject () ) )
	{
		Log ( L_ERROR, "Create download object failed" );
		return HandleDownloadFinished(ENUM_DOWNLOAD_RESULT_FAILED);
	}
	// 设置取站点信息对象的参数
	m_pDownloadPub_Info->SetAuthorization ( lpszUsername, lpszPassword );
	m_pDownloadPub_Info->m_pDownloadMTR = this;
	m_pDownloadPub_Info->SetDownloadUrl ( lpszDownloadURL );

	// 创建一个下载线程
	DWORD dwThreadId = 0;
	m_hThread = CreateThread ( NULL, 0, ::ThreadProc_DownloadMTR, LPVOID(this), 0, &dwThreadId );
	if ( !HANDLE_IS_VALID(m_hThread) )
	{
		Log ( L_WARNING, "Create download thread failed" );
		return FALSE;
	}

	return TRUE;
}

//
// 创建下载对象
//
CDownloadPub* CDownloadMTR::CreateDownloadObject ( int nCount/*=1*/ )
{
	if ( nCount < 1 ) return NULL;
	CDownloadPub *pDownloadPub = NULL;
	if ( m_csProtocolType.CompareNoCase ( "http" ) == 0 )
	{
		pDownloadPub = (CDownloadPub*)new CDownloadHttp[nCount];
	}
	else if ( m_csProtocolType.CompareNoCase ( "ftp" ) == 0 )
	{
		pDownloadPub = (CDownloadPub*)new CDownloadFtp[nCount];
	}
	else return NULL;

	return pDownloadPub;
}

//
// 删除下载对象
//
void CDownloadMTR::DeleteDownloadObject ( CDownloadPub *pDownloadPub )
{
	if ( m_csProtocolType.CompareNoCase ( "http" ) == 0 )
	{
		delete[] ( (CDownloadHttp*)pDownloadPub );
	}
	else if ( m_csProtocolType.CompareNoCase ( "ftp" ) == 0 )
	{
		delete[] ( (CDownloadFtp*)pDownloadPub );
	}
	else delete[] pDownloadPub;
}

void Callback_SaveDownloadInfo ( int nIndex, int nDownloadedSize, int nSimpleSaveSize, WPARAM wParam )
{
	CDownloadMTR *pDownloadMTR = (CDownloadMTR*)wParam;
	ASSERT ( pDownloadMTR );
	pDownloadMTR->Callback_SaveDownloadInfo ( nIndex, nDownloadedSize, nSimpleSaveSize );
}

void CDownloadMTR::Callback_SaveDownloadInfo ( int nIndex, int nDownloadedSize, int nSimpleSaveSize )
{
	if ( nIndex >= 0 && nIndex < m_nThreadCount )
	{
		m_pDownloadCellInfo[nIndex].nDownloadedSize = nDownloadedSize;
		if ( nDownloadedSize > 0 )
		{
			m_CSFor_DownloadedData.Lock();
			m_nTotalDownloadedSize_ThisTimes += nSimpleSaveSize;
			m_CSFor_DownloadedData.Unlock ();
		}
	}
}

//
// 创建多线程下载使用的对象和数据缓冲
//
BOOL CDownloadMTR::CreateDownloadObjectAndDataMTR ()
{
	DeleteDownloadObjectAndDataMTR ();

	ASSERT ( !m_pDownloadPub_MTR && m_pDownloadPub_Info );
	m_pDownloadPub_MTR = CreateDownloadObject ( m_nThreadCount );
	// 设置多线程下载使用的对象的参数
	if ( m_pDownloadPub_MTR )
	{
		for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
		{
			m_pDownloadPub_MTR[nIndex].m_nIndex = nIndex;
			m_pDownloadPub_MTR[nIndex].m_pDownloadMTR = this;
			m_pDownloadPub_MTR[nIndex].Set_SaveDownloadInfo_Callback ( ::Callback_SaveDownloadInfo, WPARAM(this) );
			m_pDownloadPub_MTR[nIndex].SetAuthorization ( m_pDownloadPub_Info->Get_UserName(), m_pDownloadPub_Info->Get_GetPassword() );
			m_pDownloadPub_MTR[nIndex].SetDownloadUrl ( m_csDownloadURL );
			if ( !m_pDownloadPub_MTR[nIndex].SetSaveFileName ( GetTempFilePath() ) )
				return FALSE;
		}
	}

	// 创建多线程下载使用的数据缓冲
	ASSERT ( !m_pDownloadCellInfo );
	m_pDownloadCellInfo = new t_DownloadCellInfo[m_nThreadCount];
	if ( m_pDownloadCellInfo )
		memset ( m_pDownloadCellInfo, 0, m_nThreadCount*sizeof(t_DownloadCellInfo) );

	if ( m_pDownloadPub_MTR != NULL && m_pDownloadCellInfo != NULL )
		return TRUE;
	Log ( L_WARNING, "Create MTR download object or buffer failed" );
	return FALSE;

}

//
// 删除多线程下载使用的对象和数据缓冲
//
void CDownloadMTR::DeleteDownloadObjectAndDataMTR()
{
	if ( m_pDownloadPub_MTR )
	{
		DeleteDownloadObject ( m_pDownloadPub_MTR );
		m_pDownloadPub_MTR = NULL;
	}
	if ( m_pDownloadCellInfo )
	{
		delete[] m_pDownloadCellInfo;
		m_pDownloadCellInfo = NULL;
	}
}

//
// 删除取站点信息的下载对象
//
void CDownloadMTR::DeleteDownloadObject_Info()
{
	if ( m_pDownloadPub_Info )
	{
		DeleteDownloadObject ( m_pDownloadPub_Info );
		m_pDownloadPub_Info = NULL;
	}
}

//
// 启动多线程下载,返回 0 表示失败,1表示成功,2表示不用下载了,因为该文件已经下载过了
//
int CDownloadMTR::StartMTRDownload ()
{
	m_dwDownloadStartTime = GetTickCount();
	DownloadNotify ( -1, NOTIFY_TYPE_START_DOWNLOAD, (LPVOID)NULL, this );
	// 先获取站点信息
	ASSERT ( m_pDownloadPub_Info );
	if ( !m_pDownloadPub_Info->GetRemoteSiteInfo () )
		return 0;
	DbgLog ( "要下载的文件大小是: %d 字节\n", m_pDownloadPub_Info->Get_FileTotalSize () );
	StandardSaveFileName ();
	CFileStatus fileStatus;

	if ( m_bForceDownload )
	{
		// 需要重新下载
		::DeleteFile ( m_csSavePathFileName );
		::DeleteFile ( GetTempFilePath() );
	}
	else
	{
		// 要保存的文件是否已经存在,且大小和创建时间一致,如果不是强制性下载,则不需要再下载了。
		if ( CFile::GetStatus(m_csSavePathFileName,fileStatus) )
		{
			if	(
				(
				fileStatus.m_mtime.GetTime() - m_pDownloadPub_Info->Get_TimeLastModified() <=2 &&
				m_pDownloadPub_Info->Get_TimeLastModified()-fileStatus.m_mtime.GetTime() <=2
				)
				&&
				fileStatus.m_size == m_pDownloadPub_Info->Get_FileTotalSize ()
				&&
				!m_bForceDownload
				)
			{
				return 2;
			}
		}
	}

	BOOL bMustCreateNullFile = TRUE;
	// 读取下载信息,如果能读到说明上次下载尚未完成
	if ( !m_bForceDownload && m_pDownloadPub_Info->Is_SupportResume() )
	{
		if ( CFile::GetStatus(GetTempFilePath(),fileStatus) &&
			fileStatus.m_size == m_pDownloadPub_Info->Get_FileTotalSize()+GetDownloadInfoWholeSize() )
		{
			if ( ReadDownloadInfo () )
				bMustCreateNullFile = FALSE;
		}
	}
	
	if ( bMustCreateNullFile )
	{
		int nFileSize = m_pDownloadPub_Info->Get_FileTotalSize();
		int nTempFileSize = nFileSize+GetDownloadInfoWholeSize();
		if ( nFileSize < 0 || !m_pDownloadPub_Info->Is_SupportResume() )
			nTempFileSize = 0;
		// 创建一个用来保存下载数据的空文件
		if ( !CreateNullFile ( GetTempFilePath(), nTempFileSize ) )
			return FALSE;
	}

	// 分配下载任务
	if ( !AssignDownloadTask () )
	{
		Log ( L_WARNING, "Assign task failed" );
		return 0;
	}

	m_dwDownloadStartTime = GetTickCount();
	return 1;
}

//
// 得到临时数据保存的路径文件名
//
CString CDownloadMTR::GetTempFilePath ()
{
	ASSERT ( !m_csSavePathFileName.IsEmpty () );
	CString csTempFileName;
	csTempFileName.Format ( "%s.~xhw~", m_csSavePathFileName );
	::SetFileAttributes ( csTempFileName, FILE_ATTRIBUTE_HIDDEN );
	return csTempFileName;
}

//
// 分配下载任务
//
BOOL CDownloadMTR::AssignDownloadTask()
{
	ASSERT ( m_pDownloadPub_Info );
	if ( !m_pDownloadPub_Info->Is_SupportResume() )
	{
		DeleteDownloadObjectAndDataMTR ();
		Log ( L_WARNING, "Site [%s] not support resume download", m_pDownloadPub_Info->Get_ServerName() );
	}
	// 文件大小未知,采用单线程
	if ( m_pDownloadPub_Info->Get_FileTotalSize () <= 0 || !m_pDownloadPub_Info->Is_SupportResume() )
	{
		if ( m_nThreadCount != 1 )
		{
			DeleteDownloadObjectAndDataMTR ();
			SetThreadCount ( 1 );
		}
	}

	if ( !DownloadInfoIsValid() || !m_pDownloadPub_MTR || !m_pDownloadCellInfo )
	{
		if ( !CreateDownloadObjectAndDataMTR () )
			return FALSE;
	}

	ASSERT ( m_pDownloadPub_MTR && m_pDownloadCellInfo );

	// 下载任务尚未分配
	if ( !DownloadInfoIsValid() )
	{
		int nWillDownloadSize = -1, nWillDownloadStartPos = 0, nNoAssignSize = 0;
		if ( m_pDownloadPub_Info->Get_FileTotalSize () > 0 )
		{
			nWillDownloadSize = m_pDownloadPub_Info->Get_FileTotalSize () / m_nThreadCount;
			// 均分后剩下的部分,让第一个线程来承担下载
			nNoAssignSize = m_pDownloadPub_Info->Get_FileTotalSize () % m_nThreadCount;
		}

		DbgLog ( "任务分配如下:--------------------\n" );
		for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
		{
			m_pDownloadCellInfo[nIndex].nWillDownloadStartPos = nWillDownloadStartPos;
			m_pDownloadCellInfo[nIndex].nWillDownloadSize = nWillDownloadSize;
			if ( nIndex == 0 && m_pDownloadPub_Info->Get_FileTotalSize () > 0 )
			{
				m_pDownloadCellInfo[nIndex].nWillDownloadSize += nNoAssignSize;
			}

			DbgLog ( "线程.%d 从 %d(0x%08x) 下载到 %d(0x%08x) 共 %d(0x%08x) 字节\n", nIndex, 
				m_pDownloadCellInfo[nIndex].nWillDownloadStartPos, m_pDownloadCellInfo[nIndex].nWillDownloadStartPos,
				m_pDownloadCellInfo[nIndex].nWillDownloadStartPos+m_pDownloadCellInfo[nIndex].nWillDownloadSize,
				m_pDownloadCellInfo[nIndex].nWillDownloadStartPos+m_pDownloadCellInfo[nIndex].nWillDownloadSize,
				m_pDownloadCellInfo[nIndex].nWillDownloadSize, m_pDownloadCellInfo[nIndex].nWillDownloadSize );

			nWillDownloadStartPos += m_pDownloadCellInfo[nIndex].nWillDownloadSize;
		}
	}


	// 启动下载任务
	for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
	{
		if ( !m_pDownloadPub_MTR[nIndex].Download ( m_pDownloadCellInfo[nIndex].nWillDownloadStartPos,
			m_pDownloadCellInfo[nIndex].nWillDownloadSize, m_pDownloadCellInfo[nIndex].nDownloadedSize ) )
			return FALSE;
	}

	m_BaseDownInfo.dwThreadCount = m_nThreadCount;
	return TRUE;
}

//
// 从下载信息文件中读取下载信息
//
BOOL CDownloadMTR::ReadDownloadInfo()
{
	CString csTempFileName = GetTempFilePath ();
	BOOL bRet = FALSE;
	CFile file;
	TRY
	{
		if ( file.Open ( csTempFileName, CFile::modeCreate|CFile::modeNoTruncate|CFile::modeReadWrite|CFile::typeBinary|CFile::shareDenyNone ) )
		{
			if ( file.Seek ( -(int)sizeof(t_BaseDownInfo), CFile::end ) == (int)(file.GetLength() - sizeof(t_BaseDownInfo)) )
			{
				if ( (UINT)file.Read ( &m_BaseDownInfo, sizeof(t_BaseDownInfo) ) == sizeof(t_BaseDownInfo) )
				{
					if ( (m_BaseDownInfo.dwThreadCount > 0 && m_BaseDownInfo.dwThreadCount <= MAX_DOWNLOAD_THREAD_COUNT)&&
						SetThreadCount ( m_BaseDownInfo.dwThreadCount ) )
					{
						if ( CreateDownloadObjectAndDataMTR () )
						{
							if ( file.Seek ( -GetDownloadInfoWholeSize(), CFile::end ) == int(file.GetLength() - GetDownloadInfoWholeSize()) )
							{
								if ( file.Read ( m_pDownloadCellInfo, sizeof(t_DownloadCellInfo)*m_nThreadCount ) == sizeof(t_DownloadCellInfo)*m_nThreadCount )
								{
									bRet = TRUE;
								}
								else
								{
									memset ( m_pDownloadCellInfo, 0, sizeof(t_DownloadCellInfo)*m_nThreadCount );
								}
							}
						}
					}
				}
			}
		}
	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -