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

📄 downloadhttp.cpp

📁 ◆◆◆ 《FTP、HTTP 多线程断点续传下载文件》◆◆◆ FlashGet、网络蚂蚁想必大家都很熟悉
💻 CPP
字号:
// DownloadHttp.cpp: implementation of the CDownloadHttp class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "NetDownMTR.h"
#include "DownloadHttp.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
//////////////////////////////////////////////////////////////////////

CDownloadHttp::CDownloadHttp()
{

}

CDownloadHttp::~CDownloadHttp()
{

}

BOOL CDownloadHttp::DownloadOnce()
{
	// 不需要下载了
	int nWillDownloadSize = Get_WillDownloadSize();				// 本次应该下载的字节数
	int nDownloadedSize = Get_DownloadedSize ();				// 已下载字节数
	if ( nWillDownloadSize > 0 && nDownloadedSize >= nWillDownloadSize )
		return DownloadEnd(TRUE);

	if ( !CDownloadPub::DownloadOnce () )
		return DownloadEnd(FALSE);

	char szTailData[NET_BUFFER_SIZE] = {0};
	int nTailSize = sizeof(szTailData);
	if ( !RequestHttpData ( TRUE, szTailData, &nTailSize ) )
		return DownloadEnd(FALSE);
	// 从HTTP服务器中读取数据,并保存到文件中
	return DownloadEnd ( RecvDataAndSaveToFile(m_SocketClient,szTailData, nTailSize) );

}

BOOL CDownloadHttp::RequestHttpData(BOOL bGet, char *szTailData/*=NULL*/, int *pnTailSize/*=NULL*/ )
{
	int nTailSizeTemp = 0;
	BOOL bRedirect = TRUE;
	while ( bRedirect )
	{
		CString csReq = GetRequestStr ( bGet );
		CString csResponse;
		nTailSizeTemp = pnTailSize?(*pnTailSize):0;
		if ( !SendRequest ( csReq, csResponse, szTailData, &nTailSizeTemp ) )
			return FALSE;
		if ( !ParseResponseString ( csResponse, bRedirect ) )
			return FALSE;
	}
	if ( pnTailSize )
		*pnTailSize = nTailSizeTemp;

	return TRUE;
}

//
// 获取远程站点信息,如:是否支持断点续传、要下载的文件大小和创建时间等
//
BOOL CDownloadHttp::GetRemoteSiteInfo()
{
	if ( !CDownloadPub::GetRemoteSiteInfo() )
		return FALSE;

	if ( !RequestHttpData ( TRUE ) )
		return FALSE;

	m_SocketClient.Disconnect ();
	return TRUE;
}

CString CDownloadHttp::GetRequestStr(BOOL bGet)
{
	CString strVerb;
	if( bGet )
		strVerb = _T("GET ");
	else
		strVerb = _T("HEAD ");
	
	CString csReq, strAuth, strRange;
	csReq  = strVerb  + m_csObject + " HTTP/1.1\r\n";

	if ( !m_csUsername.IsEmpty () )
	{
		strAuth = _T("");
		Base64Encode ( m_csUsername + ":" + m_csPassword, strAuth );
		csReq += "Authorization: Basic " + strAuth + "\r\n";
	}
	
	csReq += "Host: " + m_csServer + "\r\n";
	csReq += "Accept: */*\r\n";
	csReq += "Pragma: no-cache\r\n"; 
	csReq += "Cache-Control: no-cache\r\n";
	csReq += "User-Agent: "+m_csUserAgent+"\r\n";
	if( !m_csReferer.IsEmpty() )
		csReq += "Referer: "+m_csReferer+"\r\n";
	csReq += "Connection: close\r\n";
	
	// 指定要下载的文件范围
	CString csEndPos;
	int nWillDownloadStartPos = Get_WillDownloadStartPos ();	// 开始位置
	int nWillDownloadSize = Get_WillDownloadSize();				// 本次应该下载的字节数
	int nDownloadedSize = Get_DownloadedSize ();				// 已下载字节数
	if ( nWillDownloadSize >= 0 )
		csEndPos.Format ( "%d", nWillDownloadStartPos+nWillDownloadSize-1 );
	ASSERT ( nWillDownloadSize < 0 || nDownloadedSize < nWillDownloadSize );
	strRange.Format ( _T("Range: bytes=%d-%s\r\n"), nWillDownloadStartPos+nDownloadedSize, csEndPos );

	csReq += strRange;
	csReq += "\r\n";
	
	return csReq;
}

//
// 向服务器提交请求,并得到返回字符串
//
BOOL CDownloadHttp::SendRequest(LPCTSTR lpszReq, CString &csResponse, char *szTailData/*=NULL*/, int *pnTailSize/*=NULL*/ )
{
	m_SocketClient.Disconnect ();
	if ( !Connect () ) return FALSE;
	if ( !m_SocketClient.SendString ( lpszReq ) )
	{
		return FALSE;
	}

	for ( int i=0; ; i++ )
	{
		char szRecvBuf[NET_BUFFER_SIZE] = {0};
		int nReadSize = m_SocketClient.Receive ( szRecvBuf, sizeof(szRecvBuf) );
		if ( nReadSize <= 0 )
		{
			Log ( L_WARNING, "Receive response data failed" );
			return FALSE;
		}
		csResponse += szRecvBuf;
		char *p = strstr ( szRecvBuf, "\r\n\r\n" );
		if ( p )
		{
			if ( szTailData && pnTailSize && *pnTailSize > 0 )
			{
				p += 4;
				int nOtioseSize = nReadSize - int( p - szRecvBuf );
				*pnTailSize = MIN ( nOtioseSize, *pnTailSize );
				memcpy ( szTailData, p, *pnTailSize );
			}
#ifdef _DEBUG
			int nPos = csResponse.Find ( "\r\n\r\n", 0 );
			CString csDump;
			if ( nPos >= 0 ) csDump = csResponse.Left ( nPos );
			else csDump = csResponse;
//			TRACE ( "\n收到回应 <<<---------------------\n\n%s\n", csDump );
#endif
			break;
		}
	}

	return TRUE;
}

DWORD CDownloadHttp::GetResponseCode(CString csLineText)
{
	csLineText.MakeLower ();
	ASSERT ( csLineText.Find ( "http/", 0 ) >= 0 );
	int nPos = csLineText.Find ( " ", 0 );
	if ( nPos < 0 ) return 0;
	CString csCode = csLineText.Mid ( nPos + 1 );
	csCode.TrimLeft(); csCode.TrimRight();
	nPos = csCode.Find ( " ", 0 );
	if ( nPos < 0 ) nPos = csCode.GetLength() - 1;
	csCode = csCode.Left ( nPos );

	return (DWORD)atoi(csCode);
}

BOOL CDownloadHttp::ParseResponseString ( CString csResponseString, OUT BOOL &bRedirect )
{
	bRedirect = FALSE;
	// 获取返回代码
	CString csOneLine = GetOneLine ( csResponseString );
	DWORD dwResponseCode = GetResponseCode ( csOneLine );
	if ( dwResponseCode < 1 )
	{
		Log ( L_WARNING, "Received error response code : %s", csOneLine );
		return FALSE;
	}
	
	int nPos = 0;
	// 请求文件被重定向
	if( dwResponseCode >= 300 && dwResponseCode < 400 )
	{
		bRedirect = TRUE;
		// 得到请求文件新的URL
		CString csRedirectFileName = FindAfterFlagString ( "location:", csResponseString );
		
		// 设置 Referer
		m_csReferer = m_csDownloadUrl;
		
		// 重定向到其他的服务器
		nPos = csRedirectFileName.Find("://");
		if ( nPos >= 0 )
		{
			m_csDownloadUrl = csRedirectFileName;
			// 检验要下载的URL是否有效
			if ( !ParseURL ( m_csDownloadUrl, m_csServer, m_csObject, m_nPort, m_csProtocolType ) )
			{
				Log ( L_WARNING, "Redirect media path [%s] invalid", m_csDownloadUrl );
				return FALSE;
			}
			return TRUE;
		}
		
		// 重定向到本服务器的其他地方
		csRedirectFileName.Replace ( "\\", "/" );
		// 重定向于根目录
		if( csRedirectFileName[0] == '/' )
		{
			m_csObject = csRedirectFileName;
			DownloadNotify ( -1, NOTIFY_TYPE_GOT_REMOTE_FILENAME, (LPVOID)(LPCTSTR)(GetDownloadObjectFileName()), m_pDownloadMTR );
			return TRUE;
		}
		
		// 定向于相对当前目录
		int nParentDirCount = 0;
		nPos = csRedirectFileName.Find ( "../" );
		while ( nPos >= 0 )
		{
			csRedirectFileName = csRedirectFileName.Mid(nPos+3);
			nParentDirCount++;
			nPos = csRedirectFileName.Find("../");
		}
		for (int i=0; i<=nParentDirCount; i++)
		{
			nPos = m_csDownloadUrl.ReverseFind('/');
			if (nPos != -1)
				m_csDownloadUrl = m_csDownloadUrl.Left(nPos);
		}
		if ( csRedirectFileName.Find ( "./", 0 ) == 0 )
			csRedirectFileName.Delete ( 0, 2 );
		m_csDownloadUrl = m_csDownloadUrl+"/"+csRedirectFileName;
		
		return ParseURL ( m_csDownloadUrl, m_csServer, m_csObject, m_nPort, m_csProtocolType );
	}
	// 请求被成功接收、理解和接受
	else if( dwResponseCode >= 200 && dwResponseCode < 300 )
	{
		if ( m_nIndex == -1 )	// 主线程才需要获取文件大小的信息
		{
			// 获取 Content-Length
			CString csDownFileLen = FindAfterFlagString ( "content-length:", csResponseString );
			m_nFileTotalSize = (int) _ttoi( (LPCTSTR)csDownFileLen );
			DownloadNotify ( -1, NOTIFY_TYPE_GOT_REMOTE_FILESIZE, (LPVOID)m_nFileTotalSize, m_pDownloadMTR );
			int nWillDownloadStartPos = Get_WillDownloadStartPos ();	// 开始位置
			int nWillDownloadSize = Get_WillDownloadSize();				// 本次应该下载的字节数
			int nDownloadedSize = Get_DownloadedSize ();				// 已下载字节数
			if ( m_nFileTotalSize > 0 && nWillDownloadSize-nDownloadedSize > m_nFileTotalSize )
				Set_WillDownloadSize ( m_nFileTotalSize-nDownloadedSize );
		}
		
		// 获取服务器文件的最后修改时间
		CString csModifiedTime = FindAfterFlagString ( "last-modified:", csResponseString );
		if ( !csModifiedTime.IsEmpty() )
		{
			m_TimeLastModified = ConvertHttpTimeString(csModifiedTime);
		}

		if ( dwResponseCode == 206 )	// 支持断点续传
		{
			m_bSupportResume = TRUE;
		}
		else							// 不支持断点续传
		{
			m_bSupportResume = FALSE;
		}
		return TRUE;
	}

//	Log ( L_WARNING, "Index.%d receive invalid code : %d", m_nIndex, dwResponseCode );
	return FALSE;
}

CString CDownloadHttp::FindAfterFlagString(LPCTSTR lpszFoundStr, CString csOrg)
{
	ASSERT ( lpszFoundStr && strlen(lpszFoundStr) > 0 );
	CString csReturing, csFoundStr = GET_SAFE_STRING(lpszFoundStr);
	csFoundStr.MakeLower ();
	CString csOrgLower = csOrg;
	csOrgLower.MakeLower ();
	int nPos = csOrgLower.Find ( csFoundStr );
	if ( nPos < 0 ) return "";
	csReturing = csOrg.Mid ( nPos + csFoundStr.GetLength() );
	nPos = csReturing.Find("\r\n");
	if ( nPos < 0 ) return "";
	csReturing = csReturing.Left(nPos);
	csReturing.TrimLeft();
	csReturing.TrimRight();

	return csReturing;
}

//
// 将 HTTP 服务器表示的时间转换为 CTime 格式,如:Wed, 16 May 2007 14:29:53 GMT
//
CTime CDownloadHttp::ConvertHttpTimeString(CString csTimeGMT)
{
	CString csYear, csMonth, csDay, csTime;
	CTime tReturning = -1;
	int nPos = csTimeGMT.Find ( ",", 0 );
	if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )
		return tReturning;
	csTimeGMT = csTimeGMT.Mid ( nPos + 1 );
	csTimeGMT.TrimLeft(); csTimeGMT.TrimRight ();

	// 日
	nPos = csTimeGMT.Find ( " ", 0 );
	if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )
		return tReturning;
	csDay = csTimeGMT.Left ( nPos );
	csTimeGMT = csTimeGMT.Mid ( nPos + 1 );
	csTimeGMT.TrimLeft(); csTimeGMT.TrimRight ();

	// 月
	nPos = csTimeGMT.Find ( " ", 0 );
	if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )
		return tReturning;
	csMonth = csTimeGMT.Left ( nPos );
	int nMonth = GetMouthByShortStr ( csMonth );
	ASSERT ( nMonth >= 1 && nMonth <= 12 );
	csMonth.Format ( "%02d", nMonth );
	csTimeGMT = csTimeGMT.Mid ( nPos + 1 );
	csTimeGMT.TrimLeft(); csTimeGMT.TrimRight ();

	// 年
	nPos = csTimeGMT.Find ( " ", 0 );
	if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )
		return tReturning;
	csYear = csTimeGMT.Left ( nPos );
	csTimeGMT = csTimeGMT.Mid ( nPos + 1 );
	csTimeGMT.TrimLeft(); csTimeGMT.TrimRight ();

	// 时间
	nPos = csTimeGMT.Find ( " ", 0 );
	if ( nPos < 0 || nPos >= csTimeGMT.GetLength()-1 )
		return tReturning;
	csTime = csTimeGMT.Left ( nPos );
	csTimeGMT = csTimeGMT.Mid ( nPos + 1 );

	CString csFileTimeInfo;
	csFileTimeInfo.Format ( "%s-%s-%s %s", csYear, csMonth, csDay, csTime );
	ConvertStrToCTime ( csFileTimeInfo.GetBuffer(0), tReturning );
	return tReturning;
}

⌨️ 快捷键说明

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