分类: C/C++
2008-03-12 19:41:23
// CInternetSession在遇到一些错误时会抛出异常,因此必须包起来 TRY { CInternetSession sess ; // 统一以二进制方式下载 DWORD dwFlag = INTERNET_FLAG_TRANSFER_BINARY|INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_RELOAD ; CHttpFile * pF = (CHttpFile*)sess.OpenURL(strFilename, 1, dwFlag); ASSERT(pF); if (!pF) {AfxThrowInternetException(1);} // 得到文件大小 CString str ; pF->QueryInfo (HTTP_QUERY_CONTENT_LENGTH, str) ; int nFileSize = _ttoi(str) ; char * p = new[nFileSize] ; while (true) { // 每次下载8Kb int n = pF->Read (p, (nFileSize < 8192) ? nFileSize : 8192) ; if (n <= 0) break ; p += n ; nFileSize -= n ; } delete[] p ; delete pF ; } CATCH_ALL(e) {} END_CATCH_ALL这段代码有一个问题,在获取文件大小这个地方,对于静态网页 HTTP_QUERY_CONTENT_LENGTH 查询会返回文件大小,但对于asp,php这样的动态网页,查询会返回0。必须通过不断的调用 CHttpFile::GetLength 来一点一点累加内容,就像这样:
int n = pF->GetLength() ; while (n) { int * p = new BYTE[n] ; pF->Read (p, n) ; delete[] p ; n = pF->GetLength() ; }不过网络断线同样会让 GetLength 返回0,必须把这种情况屏蔽掉。
if (n == 0) { DWORD dw ; if (::InternetQueryDataAvailable ((HINTERNET)(*pF), &dw, 0, 0) && (dw == 0)) { // 到这里就代表文件下载成功了 } }OK,我们已经把机制摸清了,剩下就是把这些体力活全扔进线程里,又一个麻烦产生了:线程里如何向外界通知事件(开始下载,下载完成之类)呢?直接调用回调函数当然可以,但这时回调函数是置于我们的线程中,造成在回调函数中对资源的访问必须非常小心,防止多线程冲突。下一步,加锁同步...。
template使用起来非常简单,让你的窗口从它派生,然后选择你感兴趣的事件重载之即可。class FCDownloadFileWndBase : public T { public: // 默认构造函数 FCDownloadFileWndBase () {} // CDialog 构造函数 FCDownloadFileWndBase (UINT nID, CWnd* pParent) : T(nID, pParent) {} // CFormView 构造函数 FCDownloadFileWndBase (UINT nID) : T(nID) {} // 创建一个线程下载文件URL,如果URL正在下载中,此函数什么也不做立即返回 void DownloadFile (LPCTSTR strFileURL, int nPriority=THREAD_PRIORITY_IDLE) ; protected: // 检查链接最后修改时间,有些服务器会禁止查看时间,strTime为空 // 用户必须重载实现本接口,返回TRUE则继续下载文件,返回FALSE则不再下载文件 virtual BOOL DownloadFile_OnCheckTime (CString strFileURL, CString strTime) =0 ; // 当链接成功下载完成后会调用此接口 virtual void DownloadFile_OnFinished (CString strFileURL, char* pBuffer, int nLength) {} // 当IE设置代理服务器并且服务器需要帐号认证时候回调 virtual void DownloadFile_OnProxyValidate (CString strFileURL, CString& strUsername, CString& strPassword) {} // 出现错误时回调 virtual void DownloadFile_OnError (CString strFileURL) {} // 开始下载一个链接 virtual void DownloadFile_OnStartDownload (CString strFileURL) {} // 当前进度,每下载一块数据就会回调 virtual void DownloadFile_OnProgress (CString strFileURL, int nNow, int nTotal) {} };
几点说明: