我们知道,windows有关网络连接的API在wininet.h里,而在MFC里,这些API被封装成了类。
CInternetSession类:直接继承自CObject类,该类用来建立与某个Internet服务器的会话
CInternetConnection类:帮助用户管理与Internet服务器的连接,同时还提供一些函数完成和响应服务器的通信
CInternetConnection类又派生出三个类:
CHttpConnection类:管理与HTTP服务器的连接
CFtpConnection类:用于管理与FTP服务器的连接
CGopherConnection类:管理与Gopher服务器的连接
我们要实现根据URL下载并保存文件,就需要CInternetSession、CHttpConnection、CHttpFile。
CHttpFile用来做什么呢?它可以关联起URL指向的一个WEB对象,由于CHttpFile继承与CFile,我们可以像读取一个CFile文件一样,把它从HTTP服务器上下载下来。
MFC就是通过对CHttpFile对象的读写来完成与HTTP服务器的对话的,包括GET/POST提交数据,接收数据。
使用类CInternetSession 创建并初始化一个或多个同时的Internet会话。如果需要,还可描述与代理服务器的连接。如果Internet 连接必须在应用过程中保持着,可创建一个类CWinApp的CInternetSession成员。一旦已建起Internet 会话,就可调用OpenURL。CInternetSession会通过调用全局函数AfxParseURL来为分析映射URL。无论协议类型如何,CInternetSession 解释URL并管理它。它可处理由URL资源“file://”标志的本地文件的请求。如果传给它的名字是本地文件,OpenURL将返回一个指向CStdioFile对象的指针。
如果使用OpenURL在Internet服务器上打开一个URL,你可从此处读取信息。如果要执行定位在服务器上的指定的服务(例如,HTTP,FTP或Gopher)行为,必须与此服务器建立适当的连接。直接打开与指定的服务器的指定的类型的连接,请使用下列成员函数:
·GetGopherConnection |
打开与Gopher服务的连接。 |
·GetHttpConnection |
打开与HTTP服务的连接。 |
·GetFtpConnection |
打开与FTP服务的连接。 |
可使用以下代码连接到代理服务器:
INTERNET_PROXY_INFO proxyinfo;
proxyinfo.dwAccessType = INTERNET_OPEN_TYPE_PROXY;
proxyinfo.lpszProxy ="192.168.69.160:808";
proxyinfo.lpszProxyBypass = NULL;
session.SetOption(INTERNET_OPTION_PROXY,(LPVOID)&proxyinfo,
sizeof(INTERNET_PROXY_INFO));
QueryOption和SetOption允许设置会话的查询选项,如超时值、再试次数等等。Internet会话过程中,象查找或数据下载这样的事务处理会占用一定的时间。使用者可能想继续工作,或获得事务处理进程的状态信息。为解决这个问题,CInternetSession可以让查找和数据传输异步发生,允许使用者在传输结束时进行其它任务。如果要为使用者提供状态信息,或异步处理任意操作,必须设置三个条件:
1 |
在构造函数中,dwFlags必须包括INTERNET_FLAG_ASYNC。 |
2 |
在构造函数中,dwContext必须设置为1。 |
3 |
必须通过调用EnableStatusCallback来建立回调函数。 |
设置会话的查询选项可使用如下代码:
CInternetSessionsession;
session.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, 15000); // 15秒的连接超时
session.SetOption(INTERNET_OPTION_SEND_TIMEOUT, 1000); // 1秒的发送超时
session.SetOption(INTERNET_OPTION_RECEIVE_TIMEOUT, 37000); // 37秒的接收超时
session.SetOption(INTERNET_OPTION_DATA_SEND_TIMEOUT, 1000); // 1秒的发送超时
session.SetOption(INTERNET_OPTION_DATA_RECEIVE_TIMEOUT, 37000); // 37秒的接收超时
session.SetOption(INTERNET_OPTION_CONNECT_RETRIES, 10); // 10次重试
MFC类CHttpConnection管理与HTTP服务器的连接。HTTP是用MFCWinInet类实现的三个Internet服务器协议之中的一个。类CHttpConnection包含一个构造函数和一个成员函数OpenRequest,使用HTTP协议来管理与服务器的连接。要与一个HTTP服务器通讯,必须先构造一个CInternetSession的实例,然后构造一个CHttpConnection对象。不能直接构造一个CHttpConnection对象,而是调用CInternetSession::GetHttpConnection,创建CHttpConnection对象并返回其指针。
CHttpFile提供向HTTP服务器中请求和读取的功能。如果Internet会话要从一个HTTP服务器中读取数据,则必须构造一个CHttpFile实例。
可用如下代码建立与互联网上文件的连接,实现文件下载的前提,这也是文件下载的核心代码了。
CInternetSession session;
CHttpConnection* pHttpConnection = NULL;
CHttpFile* pHttpFile = NULL;
pHttpConnection = session.GetHttpConnection(strServer, wPort);
CString strServer, strObject;
INTERNET_PORT wPort;
DWORD dwType;
AfxParseURL(“待下载文件的URL”, dwType, strServer, strObject, wPort);
pHttpConnection = session.GetHttpConnection(strServer, wPort);
pHttpFile = pHttpConnection->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject);
附(文件下载函数)
bool XXX::Download(const CString& strFileURLInServer, //待下载文件的URLCHttpFile* pHttpFile = NULL;
}
利用以上的下载函数,是可以实现下载功能的,需要说明一下的是,要测试下载功能需要有一个下载的连接地址URL,也许你还不清除这个要怎么弄来,这里也说明下:URL不是云盘上的目录地址,而是下载链接,打开下载管理器,右键即可复制了
我们来做个简单的下载工具,模拟这个Sogou的下载管理器。
有了下载函数,主要还要做以下功能:
1、进度条
2、图片按钮
3、开启定时器来显示下载进度
4、控件背景
5、下载类型的文件图标
下面就来解决这些问题:
1、进度条
文件下载是比较耗能的工作,因此是不适合在下载函数里显示进度条进度的,事实上,进度条显示的,都是时间比较长的工作,不然干嘛需要用进度条来显示进度从而告诉我们程序没卡死呢,因此,进度条的显示都是开线程或者用线程回调来做的。
获得进度条id
先定义 CProgressCtrl m_ctrProgress; //进度条控制函数
再在下面的地方添加:
void CdowmloadDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_PROGRESS1, m_ctrProgress);
}
在头文件添加声明
DWORD _stdcall ShowThread(LPVOIDlpParam);
在cpp文件写下面的线程处理函数
DWORD _stdcall ShowThread(LPVOIDlpParam)
{
m_ctrProgress.SetRange32(0,filesize);//设置进度条的上下界
m_ctrProgress.SetPos(dwTotalWrite);//当前进度
return 0;
}
在文件下载的函数循环里创建线程
CreateThread(NULL,0,ShowThread,NULL,0,NULL);
2、图片按钮
图片按钮的方法有几种的:
1、按钮贴图,然后再改变按钮样式来实现
按钮属性里的Bitmap要选为TRUE‘
(1)首先建立位图句柄,
2、自己绘制
按钮属性里的Owner Draw要选为TRUE,按钮自定义绘图,需要用户在OndrawItem消息处理函数中绘制按钮的外观。代码大概用下面的
CRect rect;
Btn_start->GetWindowRect(rect);
ScreenToClient(rect);//
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);//其中IDB_BITMAP是位图名称
CBrush brush(&bitmap);
CClientDC dc(this);
dc.FillRect(rect,&brush);
3、用MFC现成的类CBitmapButton,我就是用这个来实现的,方便点,就是有点局限
(1)在头文件中声明
CBitmapButtonbmpBtnStart,bmpBtnStop,bmpBtnCancle;
(2)在cpp文件中的BOOL CxxxDlg::OnInitDialog()中
bmpBtnStart.LoadBitmaps(IDB_start,IDB_startOn); //绑定图片,IDB_BTN_LOGIN为图片资源名,资源需要事先导入
bmpBtnStart.SubclassDlgItem(IDC_BTN_START,this); //绑定控件ID
bmpBtnStart.SizeToContent(); //设置控件大小与图片相同
bmpBtnStop.LoadBitmaps(IDB_stop,IDB_stopOn);
bmpBtnStop.SubclassDlgItem(IDC_BTN_STOP,this);
bmpBtnStop.SizeToContent();
bmpBtnCancle.LoadBitmaps(IDB_close,IDB_closeOn);
bmpBtnCancle.SubclassDlgItem(IDC_BTN_cancel,this);
bmpBtnCancle.SizeToContent();
当然需要先在资源文件里加载图片资源。
3、开启定时器来显示下载进度
(1)头文件中什么消息响应函数void OnTimer(UINT_PTR nIDEvent);
(2)cpp中首先要添加消息 ON_WM_TIMER()
(3)cpp中定时器响应函数
void CxxxDlg::OnTimer(UINT_PTR nIDEvent)
{
switch (nIDEvent)
{
case 1: // 计时器1
.... // 处理的代码
KillTimer(1); // 删除此计时器,否则计时器中断完后会自动重新开始计时,到下一次中断时还会发生新的中断
break;
case 2: // 计时器2
.... // 处理的代码
break;
default:
MessageBox("default: KillTimer");
KillTimer(nIDEvent);
break;
}
}
(4)启动一个1秒钟的定时器SetTimer(1,1000, NULL);
关闭该定时器 KillTimer(1);
4、控件背景
(1)头文件里声明 HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
(2)cpp里添加重载消息 ON_WM_CTLCOLOR()
(3)重载函数
HBRUSH CdowmloadDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
switch(nCtlColor)
{
case CTLCOLOR_STATIC: //静态文本控件
{
// pDC->SetBkMode(TRANSPARENT);
// //pDC->SetTextColor(RGB(255,255,0)); //设置字体颜色
// //pWnd->SetFont(cfont); //设置字体
// HBRUSH B = CreateSolidBrush(RGB(125,125,255)); //创建画刷
//pDC->SetTextColor(RGB(250,250,250));
pDC->SetBkColor(RGB(250,250,250));
return (HBRUSH)staticBrush;
}
// case CTLCOLOR_BTN: //按钮控件
// {
// pDC->SetBkMode(TRANSPARENT);
// /* pDC->SetTextColor(RGB(255,255,0));
// pWnd->SetFont(cfont); */
// HBRUSH B = CreateSolidBrush(RGB(125,125,255));
// return (HBRUSH) B;
// }
}
return hbr;
}
5、下载类型的文件图标
(1)添加一个picture control的控件,其实它用起来和static控件差不多
(2)判断待下载的图片类型,然后用对应的图标显示就可以了。下面为加载图片代码
CStatic *pStatic = (CStatic *)GetDlgItem(IDC_picture);
//加载资源图片
HBITMAP hBitmap;
hBitmap = LoadBitmap(AfxGetInstanceHandle(),
MAKEINTRESOURCE(IDB_zip)); // IDB_BITMAP_TEST为资源图片ID
程序效果图:
文件下载的源代码http://download.csdn.net/detail/sns1991sns/9150529