1、HTTP服务器上断点下载文件 从HTTP服务器上下载一个文件有很多方法,“热心”的微软提供了 WinInet 类,用起来也很方便。当然,我们也可以自己实现这些功能,通过格式化请求头很容易就能实现断点续传和检查更新等等功能 。 1. 连接主机 2. 格式化请求头 3. 设置接收,发送超时 要想从服务器下载文件,首先要向服务器发送一个请求。HTTP 请求头由若干行字符串组成。下面结合实例说说 HTTP 请求头的格式。假设要下载 这个网页 ,那么请求头的写法如下: 第1行:方法,请求的内容,HTTP协议的版本 下载一般可以用GET方法,请求
2、的内容是“/index.html”,HTTP协议的版本是指浏览器支持的版本,对于下载软件来说无所谓,所以用1.1版 “HTTP/1.1”; “GET /index.html HTTP/1.1” 第2行:主机名,格式为“Host:主机” 在这个例子中是:“Host:” 第3行:接受的数据类型,下载软件当然要接收所有的数据类型,所以: “Accept:*/*” 第4行:指定浏览器的类型 有些服务器会根据客户服务器种类的不同会增加或减少一些内容,在这个例子中可以这样写: “User-Agent:Mozilla/4.0 (compatible; MSIE 5.00; Win
3、dows 98)” 第5行:连接设置 设定为一直保持连接:“Connection:Keep-Alive” 第6行:若要实现断点续传则要指定从什么位置起接收数据,格式如下: “Range: bytes=起始位置 - 终止位置” 比如要读前500个字节可以这样写:“Range: bytes=0 - 499”;从第 1000 个字节起开始下载: “Range: bytes=999 -” 最后,别忘了加上一行空行,表示请求头结束。整个请求头如下: GET /index.html HTTP/1.1 Host: Accept:*/* User-Agent:Mozilla/4.0
4、compatible; MSIE 5.00; Windows 98) Connection:Keep-Alive 下面用例子看看如何进行断点的下载吧 // DownloadFile.h: interface for the CDownloadFile class. // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_DOWNLOADFILE_H__E9A59779_BEF9_4A78_8D0E_ED8C9498E07C__INC
5、LUDED_) #define AFX_DOWNLOADFILE_H__E9A59779_BEF9_4A78_8D0E_ED8C9498E07C__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #define NOTIFY_MSG_WPARAM_GENDOWNFILEID 0x01 #define NOTIFY_MSG_LOW_WPARAM_FULLSIZE 0x10 #define NOTIFY_MSG_LOW_WP
6、ARAM_CURRENTSIZE 0x20 #define NOTIFY_MSG_LOW_WPARAM_DOWNSIZE 0x30 #define NOTIFY_MSG_LOW_WPARAM_DOWNSPEED 0x40 class CDownloadFile { public: BOOL OpenRedirectHttpURL(CString &strOldLocation,CInternetSession &cSession); BOOL DownLoadFile(LPC
7、TSTR lpFileURL,LPCTSTR lpSaveFile); CDownloadFile(); virtual ~CDownloadFile(); LPCTSTR GetSavedFileName() { return m_strSaveToFile;} LPCTSTR GetDownURL() { return m_strFileURL;} public: WORD GenFileID(); void RegisterNotifyWindow(DWORD dwThreadID,HWND hWnd,DWORD dwMs
8、g); BOOL GetUNCFile(); bool m_bForceReload;` DWORD m_TimeOut; WORD m_wFileID; protected: DWORD m_dwThreadID; void PostNotifyMessage(WPARAM wParam, LPARAM lParam); DWORD m_dwMsgID; HWND m_hNotify; BOOL GetFtpFile(CInternetSession &cSession); BOOL G
9、etHttpFile(CInternetSession &cSession); CString m_strTmpFileName; CString m_strFileURL; CString m_strSaveToFile; CString m_rawHeaders; float m_transferRate; DWORD m_infoStatusCode; }; #endif // !defined(AFX_DOWNLOADFILE_H__E9A59779_BEF9_4A78_8D0E_ED8C9498E07C
10、INCLUDED_) // DownloadFile.cpp: implementation of the CDownloadFile class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "DownloadFile.h" #pragma comment( lib,"Wininet.lib" ) #include "shlwapi.h" #pragma comment( lib,"shlwapi
11、lib" ) #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #define BUFFER_SIZE 4095 const TCHAR szHeaders[] = _T("Accept: */*\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)\r\n"); ////////////////////////////////////////////
12、////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CDownloadFile::CDownloadFile() { m_TimeOut = 0; m_bForceReload = true; m_dwThreadID = 0; m_hNotify = NULL; m_dwMsgID = 0; m_wFileID = 0;
13、} CDownloadFile::~CDownloadFile() { } BOOL CDownloadFile::DownLoadFile(LPCTSTR lpFileURL, LPCTSTR lpSaveFile) { BOOL bRet = FALSE; if ( !::PathIsURL(lpFileURL) ) { return bRet; } m_strSaveToFile = lpSaveFile; m_strFileURL = lpFileURL; m_strTmpFi
14、leName = lpSaveFile; m_strTmpFileName += _T(".df!"); CString strServer,strObject;INTERNET_PORT nPort; CString strAgentCaption = _T("Update Download ") ; strAgentCaption += ::PathFindFileName(lpSaveFile); DWORD dwFlags = 0; InternetGetConnectedState(&dwFlags, 0); C
15、InternetSession session (strAgentCaption, 1, (dwFlags & INTERNET_CONNECTION_PROXY) == INTERNET_CONNECTION_PROXY ? INTERNET_OPEN_TYPE_PRECONFIG : INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY, NULL, NULL, 0); AfxParseURL(m_strFileURL,dwFlags,strServer,strObject,nPort);
16、if (m_TimeOut != 0) session.SetOption(INTERNET_OPTION_DATA_RECEIVE_TIMEOUT, m_TimeOut); if( !m_wFileID ) m_wFileID = GenFileID(); PostNotifyMessage(NOTIFY_MSG_WPARAM_GENDOWNFILEID,m_wFileID); try { if ( dwFlags== AFX_INET_SERVICE_HTTP ) {
17、 bRet = GetHttpFile(session); } else if( dwFlags== AFX_INET_SERVICE_FTP ) { bRet = GetFtpFile(session); } else if( dwFlags== AFX_INET_SERVICE_FILE ) { if( UrlIsFileUrl(m_strFileURL) ) bRet = Get
18、UNCFile(); } else { ; } } catch (CException* pEx) { TCHAR szErrorMsg[MAX_PATH] = {0}; pEx->GetErrorMessage(szErrorMsg, MAX_PATH); TRACE( _T("Exception: %s\n") , szErrorMsg); pEx->Delete(); }
19、 session.Close(); m_wFileID = 0; if (bRet) { if (!::MoveFileEx(m_strTmpFileName,m_strSaveToFile,MOVEFILE_REPLACE_EXISTING) ) { Sleep(1000); ::MoveFileEx(m_strTmpFileName,m_strSaveToFile,MOVEFILE_REPLACE_EXISTING); } }
20、 return bRet; } BOOL CDownloadFile::GetHttpFile(CInternetSession &cSession) { BOOL bRet = FALSE; CFile m_TmpFile; CFileException fileException; if ( !m_TmpFile.Open (m_strTmpFileName, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeReadWrite
21、 | CFile::shareDenyWrite | CFile::typeBinary, &fileException ) ) { TRACE( _T("Open File failed: %d\n"), fileException.m_cause ); return bRet; } CString strRangeQuest; if (m_TmpFile.GetLength()>0) { PostNotifyMessage(MAKEWPARAM(NOTIFY_
22、MSG_LOW_WPARAM_CURRENTSIZE,m_wFileID),m_TmpFile.GetLength()); m_TmpFile.SeekToEnd(); strRangeQuest.Format( _T("%sRange: bytes=%d-\r\n"), szHeaders,m_TmpFile.GetLength()); } else strRangeQuest = szHeaders; DWORD dwCount = 0; CHttpFile* pFile = NULL;
23、 CString strTmpURL = m_strFileURL; try { DWORD dwFlags = INTERNET_FLAG_TRANSFER_BINARY |INTERNET_FLAG_DONT_CACHE |INTERNET_FLAG_PRAGMA_NOCACHE ; if (m_bForceReload) { dwFlags |= INTERNET_FLAG_RELOAD; }
24、 //Here Find URLFile Redirect. // OpenRedirectHttpURL(strTmpURL,cSession); pFile = (CHttpFile*) cSession.OpenURL(strTmpURL, 1, dwFlags,strRangeQuest, -1); } catch (CInternetException* e) { TCHAR szCause[MAX_PATH] = {0}; e->GetErrorMessage(szCause
25、 MAX_PATH); e->Delete(); delete pFile; pFile = NULL; return bRet; } COleDateTime startTime = COleDateTime::GetCurrentTime(); DWORD dwHttpFileSize = 0; if (pFile) { BYTE buffer[BUFFER_SIZE+1] = {0}; try {
26、 UINT nRead = 0; pFile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,dwHttpFileSize); PostNotifyMessage(MAKEWPARAM(NOTIFY_MSG_LOW_WPARAM_FULLSIZE,m_wFileID),dwHttpFileSize); TRACE( _T("Totoal Length is %d\n"), dwHttpFileSize ); dw
27、Count = 0; do { nRead = pFile->Read(buffer, BUFFER_SIZE); if (nRead > 0) { buffer[nRead] = 0; m_TmpFile.Write(buffer,nRead); COleDateTimeS
28、pan elapsed = COleDateTime::GetCurrentTime() - startTime; double dSecs = elapsed.GetTotalSeconds(); PostNotifyMessage(MAKEWPARAM(NOTIFY_MSG_LOW_WPARAM_DOWNSIZE,m_wFileID),dwCount); if (dSecs > 0.0) {
29、 dwCount += nRead; m_transferRate = (float) ( dwCount / 1024.0 / dSecs ); TRACE("Read %d bytes (%0.1f Kb/s)\n", dwCount, m_transferRate ); } else { TRACE(
30、"Read %d bytes\n", dwCount); m_transferRate = (float) ( dwCount / 1024.0 ); } PostNotifyMessage(MAKEWPARAM(NOTIFY_MSG_LOW_WPARAM_DOWNSPEED,m_wFileID),(LPARAM)m_transferRate); } } while (nRea
31、d > 0); bRet = TRUE; } catch (CFileException *e) { TCHAR szCause[MAX_PATH] = {0}; e->GetErrorMessage(szCause, MAX_PATH); TRACE("ErrorMsg : %s\n", szCause); e->Delete(); delete pFile;
32、 m_TmpFile.Close(); return FALSE; } pFile->QueryInfoStatusCode(m_infoStatusCode); pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS ,m_rawHeaders); pFile->Close(); m_TmpFile.Close(); delete pFile; } return bRet; }
33、BOOL CDownloadFile::OpenRedirectHttpURL(CString &strOldLocation,CInternetSession &cSession) { BOOL bRet = FALSE; CHttpFile *pFile = NULL; CHttpConnection* pServer = NULL; CString strServerName,strObject; INTERNET_PORT nPort = 0; DWORD dwServiceType = 0; if (
34、AfxParseURL(strOldLocation, dwServiceType, strServerName, strObject, nPort) || dwServiceType != INTERNET_SERVICE_HTTP) { TRACE( _T("Not A Http Quest!\n") ); return bRet; } pServer = cSession.GetHttpConnection(strServerName, nPort); pFile =
35、pServer->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject, NULL, 1, NULL, NULL, INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_AUTO_REDIRECT); pFile->AddRequestHeaders(szHeaders); pFile->SendRequest(); DWORD dwRet; pFile->QueryInfoStatusCode(dwRet
36、); // if access was denied, prompt the user for the password if (dwRet == HTTP_STATUS_DENIED) { DWORD dwPrompt; dwPrompt = pFile->ErrorDlg(NULL, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | FLAGS_ERROR_UI_FLAGS_CHAN
37、GE_OPTIONS, NULL); // if the user cancelled the dialog, bail out if (dwPrompt != ERROR_INTERNET_FORCE_RETRY) { TRACE( _T("Access denied: Invalid password\n") ); // close up the redirected site pFile->Close();
38、 delete pFile; pServer->Close(); delete pServer; return bRet; } pFile->SendRequest(); pFile->QueryInfoStatusCode(dwRet); } // were we redirected? // these response status codes come from W
39、ININET.H if (dwRet == HTTP_STATUS_MOVED || dwRet == HTTP_STATUS_REDIRECT || dwRet == HTTP_STATUS_REDIRECT_METHOD) { CString strNewLocation; pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strNewLocation); int nPlace = strNewLocation.Find(_T(
40、"Location: ")); if (nPlace == -1) { TRACE( _T("Error: Site redirects with no new location\n") ); // close up the redirected site pFile->Close(); delete pFile; pServer->Close(); delete pServer;
41、 return bRet; } strNewLocation = strNewLocation.Mid(nPlace + 10); nPlace = strNewLocation.Find('\n'); if (nPlace > 0) strNewLocation = strNewLocation.Left(nPlace); strOldLocation = strNewLocation; } if ( dwRet == HTTP_STATUS_
42、OK ) { bRet = TRUE; } // close up the redirected site pFile->Close(); delete pFile; pServer->Close(); delete pServer; return bRet; } BOOL CDownloadFile::GetFtpFile(CInternetSession &cSession) { BOOL bRet = FALSE; CFile m_TmpFile;
43、 CFileException fileException; if ( !m_TmpFile.Open (m_strTmpFileName, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeReadWrite | CFile::shareDenyWrite | CFile::typeBinary, &fileException ) ) { TRACE( _T("Open File failed: %d\n"), file
44、Exception.m_cause ); return bRet; } DWORD dwCount = 0; CFtpConnection *pFtpConn = NULL; CInternetFile *pFile = NULL; try { CString strServerName,strObject,strUserName,strPassword; INTERNET_PORT nPort = 0; DWORD dwServiceType = 0;
45、 CString strRestPointCommand; if (!AfxParseURLEx(m_strFileURL, dwServiceType, strServerName, strObject, nPort, strUserName, strPassword) || dwServiceType != INTERNET_SERVICE_FTP) { TRACE( _T("Not A Ftp Quest!\n") ); }
46、 // CFtpConnection ERROR_INTERNET_NO_DIRECT_ACCESS CInternetSession if (strUserName.IsEmpty()) pFtpConn = cSession.GetFtpConnection(strServerName,NULL,NULL,nPort,m_bForceReload); else pFtpConn = cSession.GetFtpConnection(strServerName,strUserName,strP
47、assword,nPort,m_bForceReload); if (m_TmpFile.GetLength()) { PostNotifyMessage(MAKEWPARAM(NOTIFY_MSG_LOW_WPARAM_CURRENTSIZE,m_wFileID),m_TmpFile.GetLength()); m_TmpFile.SeekToEnd(); strRestPointCommand.Format( _T("REST %d"), m_TmpFile.GetLength
48、)); //strRestPointCommand.Format( _T("ls") ); if ( !FtpCommand((*pFtpConn), FALSE, FTP_TRANSFER_TYPE_ASCII, strRestPointCommand, 0, 0) ) { TRACE( _T("FtpCommand failed, error: %d\n"), GetLastError()); m_TmpFile.S
49、eekToBegin(); } } if (pFtpConn) { pFile = pFtpConn->OpenFile(strObject); } } catch (CInternetException* e) { TCHAR szCause[MAX_PATH] = {0}; e->GetErrorMessage(szCause, MAX_PATH); e->Delete(); delete pFile; delete pFtpConn; return bRet; } COleDateTime startTime = COleDateTime::GetCurrentTime(); DWORD dwFtpFileSize = 0; if (pFile) { BYTE buffer[BUFFER_SIZE+1]






