ftp文件上传下载模块概述.docx
《ftp文件上传下载模块概述.docx》由会员分享,可在线阅读,更多相关《ftp文件上传下载模块概述.docx(93页珍藏版)》请在冰豆网上搜索。
ftp文件上传下载模块概述
ftp文件上传下载模块概述
1.1开发背景
如果用户不使用FTP客户端软件,而是直接登录到FTP服务器来上传或下载FTP文件,会存在许多不足。
例如,在同时下载多个文件时,如何暂停或继续某个文件的下载,如果下载大量文件,如何在任务下载完成后关闭计算机。
这就是FTP客户端软件的作用,也是笔者设计本章程序的主要原因。
1.2需求分析
FTP文件上传下载模块的主要功能是实现FTP文件的多任务上传、下载,对于实现FTP文件的上传和下载,可以使用MFC提供的WinInet相关类非常方便地实现,模块的难点在于多任务,需要为每个上传或下载的任务维护一个单独的线程,用户可以暂停和取消任务。
在关键技术部分笔者将介绍如何实现FTP文件多任务下载。
1.3模块预览
FTP文件上传下载模块只包含一个主对话框,但是主对话框却由登录信息栏、工具栏、本地信息窗口、远程FTP服务器信息窗口和任务列表共5个子窗口构成。
下面分别给出各个窗口的效果图。
FTP文件上传下载模块主窗口效果图如图1所示。
图1FTP文件上传下载模块主窗口
登录信息栏效果如图2所示
工具栏窗口效果如图3所示。
图3工具栏窗口
本地信息窗口效果图如图4所示。
图4本地信息窗口
远程FTP服务器信息窗口效果如图5所示。
图5远程FTP服务器信息窗口
任务列表窗口运行效果如图6所示。
图6任务列表窗口
关键技术
1.1设计类似于资源管理器的列表视图控件
在设计FTP文件上传下载模块时,首先需要确定采用何种方式显示本地和FTP服务器上的目录和文件。
为了模仿Windows资源管理器的效果,笔者采用了列表视图控件——CListCtrl来实现目录和文件的显示。
但是,MFC提供的默认的CListCtrl无法实现Windows资源管理器的效果,我们必须重新设计一个列表视图控件。
该控件需要具备的功能有以本地系统默认的图标显示目录和文件的图标,在控件中双击某一个目录将进入子目录,按“BackSpace”键将返回上一级目录,实现对某一列的升序、降序排列,并以箭头标识。
控件的设计效果如图13.4所示。
在设计控件之前,读者需要对CListCtrl控件有所了解。
CListCtrl控件主要有两部分构成,第一部分是列头部分,由CHeaderCtrl控件构成,第二部分是表格部分。
当在列头部分绘制排序箭头时,实际上是在CHeaderCtrl控件上进行的。
下面介绍控件的详细设计过程。
首先设计列头控件,因为需要绘制排序列的标记。
(1)从CHeaderCtrl类派生一个子类——CSortHeaderCtrl,向该类中添加成员变量。
(1)从CHeaderCtrl类派生一个子类——CSortHeaderCtrl,向该类中添加成员变量。
intm_nSortColumn;//排序列
BOOLm_bAscend;//是否为升序
(2)在构造函数中初始化成员变量。
CSortHeaderCtrl:
:
CSortHeaderCtrl()
{
m_nSortColumn=-1;
m_bAscend=TRUE;
}
(3)向CSortHeaderCtrl类中添加SetSortColomn方法,用于设置排序列和排序列的自绘风格。
voidCSortHeaderCtrl:
:
SetSortColomn(intnColumn,BOOLbAscend)
{
m_nSortColumn=nColumn;//设置排序列
m_bAscend=bAscend;//设置排序方式
HD_ITEMhItem;
hItem.mask=HDI_FORMAT;
GetItem(nColumn,&hItem);//获取列信息
hItem.fmt|=HDF_OWNERDRAW;//设置列自绘风格
SetItem(nColumn,&hItem);//设置列信息
Invalidate();//更新控件
}
(4)改写CSortHeaderCtrl类的DrawItem方法,根据排序方式绘制排序列的箭头符号。
voidCSortHeaderCtrl:
:
DrawItem(LPDRAWITEMSTRUCTlpDrawItemStruct)
{
CDCdc;//定义设备上下文
dc.Attach(lpDrawItemStruct->hDC);//附加设备上下文句柄
constintnSavedIndex=dc.SaveDC();//保存设备上下文
CRectrc(lpDrawItemStruct->rcItem);//获取当前列区域
CBrushbrush(GetSysColor(COLOR_3DFACE));//定义背景画刷
dc.FillRect(rc,&brush);//填充画刷
TCHARszText[256];//定义字符数组,存储列文本
HD_ITEMhditem;
hditem.mask=HDI_TEXT|HDI_FORMAT;
hditem.pszText=szText;
hditem.cchTextMax=255;
GetItem(lpDrawItemStruct->itemID,&hditem);//获取当前的项目信息
UINTuFormat=DT_SINGLELINE|DT_NOPREFIX|DT_NOCLIP|
DT_VCENTER|DT_END_ELLIPSIS;//设置绘制的文本格式
if(hditem.fmt&HDF_CENTER)
uFormat|=DT_CENTER;
elseif(hditem.fmt&HDF_RIGHT)
uFormat|=DT_RIGHT;
else
uFormat|=DT_LEFT;
if(lpDrawItemStruct->itemState==ODS_SELECTED)//列是否被选中
{
rc.left++;//调整列的区域
rc.top+=2;
rc.right++;
}
CRectrcIcon(lpDrawItemStruct->rcItem);//定义箭头显示区域
constintiOffset=(rcIcon.bottom-rcIcon.top)/4;
if(lpDrawItemStruct->itemID==(UINT)m_nSortColumn)
rc.right-=3*iOffset;
rc.left+=iOffset;
rc.right-=iOffset;
if(rc.leftdc.DrawText(szText,-1,rc,uFormat);//绘制列文本
//绘制箭头
if(lpDrawItemStruct->itemID==(UINT)m_nSortColumn)
{
CPenpenLight(PS_SOLID,1,GetSysColor(COLOR_3DHILIGHT));//定义浅颜色画笔
CPenpenShadow(PS_SOLID,1,GetSysColor(COLOR_3DSHADOW));//定义深颜色画笔
CPen*pOldPen=dc.SelectObject(&penLight);//选中画笔
if(m_bAscend)//绘制向上的箭头
{
dc.MoveTo(rcIcon.right-2*iOffset,iOffset);
dc.LineTo(rcIcon.right-iOffset,rcIcon.bottom-iOffset-1);
dc.LineTo(rcIcon.right-3*iOffset-2,rcIcon.bottom-iOffset-1);
dc.SelectObject(&penShadow);
dc.MoveTo(rcIcon.right-3*iOffset-1,rcIcon.bottom-iOffset-1);
dc.LineTo(rcIcon.right-2*iOffset,iOffset-1);
}
else//绘制向下的箭头
{
dc.MoveTo(rcIcon.right-iOffset-1,iOffset);
dc.LineTo(rcIcon.right-2*iOffset-1,rcIcon.bottom-iOffset);
dc.SelectObject(&penShadow);
dc.MoveTo(rcIcon.right-2*iOffset-2,rcIcon.bottom-iOffset);
dc.LineTo(rcIcon.right-3*iOffset-1,iOffset);
dc.LineTo(rcIcon.right-iOffset-1,iOffset);
}
dc.SelectObject(pOldPen);//恢复原来选择的画笔
}
dc.RestoreDC(nSavedIndex);//恢复之前的设备上下文
dc.Detach();//从设备上下文中分离设备上下文句柄
}
在设计完列头控件之后,下面开始设计列表视图控件。
(1)从CListCtrl类派生一个子类——CSortListCtrl,向该类中添加如下成员变量。
CStringm_BaseDir;//基目录
CStringm_CurDir;//记录当前列表中文件的目录
intm_nListType;//列表类型,0表示显示本地信息,1,表示显示FTP服务器信息,默认为0
CInternetSessionm_Session;//Internet会话
CStringm_FtpServer,m_Port,m_User,m_Password;//FTP服务器,端口号,用户名和密码
CSortHeaderCtrlm_ctlHeader;//列头
intm_nNumColumns;//列数
intm_nSortColumn;//排序列
BOOLm_bAscend;//是否升序排列
(2)定义一个类CItemData,描述项目的额外数据,主要用于记录某一行各个列的文本。
当排序时,需要根据排序函数的参数获取排序列的文本以便进行比较。
classCItemData
{
public:
CItemData()//构造函数
{
m_ColumnTexts=NULL;
m_dwData=0;
}
LPTSTR*m_ColumnTexts;//记录当前行所有列文本
DWORDm_dwData;//视图项数据
private:
//禁止复制
CItemData(constCItemData&);
CItemData&operator=(constCItemData&);
};
(3)向CSortListCtrl类中添加SetItemDataList方法,为项目关联一个自定义的数据结构——CItemData。
在进行排序时需要根据该数据结构获取排序列的文本。
BOOLCSortListCtrl:
:
SetItemDataList(intiItem,LPTSTR*pchTexts)
{
if(CListCtrl:
:
GetItemData(iItem)==NULL)//判断关联的数据是否为空
{
CItemData*pItemData=newCItemData();//构建一个CItemData对象
pItemData->m_ColumnTexts=pchTexts;//设置列文本
//设置项目数据
returnCListCtrl:
:
SetItemData(iItem,(DWORD)pItemData);
}
}
(4)向CSortListCtrl类中添加GetItemDataList方法,获取项目关联的数据。
LPTSTR*CSortListCtrl:
:
GetItemDataList(intiItem)const
{
ASSERT(iItemCItemData*pItemData=(CItemData*)CListCtrl:
:
GetItemData(iItem);//获取关联数据
returnpItemData->m_ColumnTexts;//返回列文本
}
(5)向CSortListCtrl类中添加SetItemText方法,设置项目的文本,同时修改关联的CItemData结构的列文本。
BOOLCSortListCtrl:
:
SetItemText(intnItem,intnSubItem,LPCTSTRlpszText)
{
if(!
CListCtrl:
:
SetItemText(nItem,nSubItem,lpszText))//调用基类的方法设置文本
returnFALSE;
LPTSTR*pszTexts=GetItemDataList(nItem);//获取行各列文本
LPTSTRpszText=pszTexts[nSubItem];//获取当前列文本
delete[]pszText;//释放文本数据
pszText=newTCHAR[lstrlen(lpszText)+1];
lstrcpy(pszText,lpszText);//重新设置文本
pszTexts[nSubItem]=pszText;
returnTRUE;
}
(6)向CSortListCtrl类中添加AddItem方法,用于添加新行,设置行各列文本。
intCSortListCtrl:
:
AddItem(LPCTSTRpszText,...)
{
intnIndex=InsertItem(GetItemCount(),pszText);//添加行,返回行索引
LPTSTR*pszColumnTexts=newLPTSTR[m_nNumColumns];//记录各列文本
pszColumnTexts[0]=newTCHAR[lstrlen(pszText)+1];
lstrcpy(pszColumnTexts[0],pszText);//设置第一列文本
va_listlist;
va_start(list,pszText);
for(intnColumn=1;nColumn{
pszText=va_arg(list,LPCTSTR);
CListCtrl:
:
SetItem(nIndex,nColumn,LVIF_TEXT,pszText,0,0,0,0);
pszColumnTexts[nColumn]=newTCHAR[lstrlen(pszText)+1];
lstrcpy(pszColumnTexts[nColumn],pszText);
}
va_end(list);
SetItemDataList(nIndex,pszColumnTexts);//设置行关联数据
returnnIndex;
}
(7)向CSortListCtrl类中添加SetColumns方法,向列表视图控件中添加列,设置列文本。
该方法的主要作用是可以一次添加多列,并且可以指定列的宽度。
//设置列,格式为文本,宽度;文本,宽度;......
BOOLCSortListCtrl:
:
SetColumns(constCString&strHeadings)
{
intnStart=0;
for(;;)
{
intnComma=strHeadings.Find(_T(','),nStart);//查找“,”标记
if(nComma==-1)
break;
CStringstrHeading=strHeadings.Mid(nStart,nComma-nStart);//获取文本
nStart=nComma+1;//掠过“,”
intnSemiColon=strHeadings.Find(_T(';'),nStart);//查找“;”
if(nSemiColon==-1)//查找到了结尾
nSemiColon=strHeadings.GetLength();
intnWidth=atoi(strHeadings.Mid(nStart,nSemiColon-nStart));//获取宽度
nStart=nSemiColon+1;//指向下一列信息
if(InsertColumn(m_nNumColumns++,strHeading,LVCFMT_LEFT,nWidth)==-1)
returnFALSE;//插入列
}
returnTRUE;
}
(8)向CSortListCtrl类中添加GetIconFromExtendedName方法,根据文件的类型获取系统对应的文件图标。
GetIconFromExtendedName方法首先判断参数是文件还是目录,如果是文件,则调用SHGetFileInfo函数并传递SHGFI_USEFILEATTRIBUTES标记来依据文件类型返回图标,如果为目录,则以当前目录为参数,调用SHGetFileInfo函数获取系统目录的图标。
//根据文件类型获取图标
HICONCSortListCtrl:
:
GetIconFromExtendedName(LPCTSTRlpName)
{
SHFILEINFOshInfo;//定义外壳文件信息
intnIcon=0;
CStringextension=lpName;
CStringcsName="text"+extension;//设置一个临时的文件名
intnPos=csName.ReverseFind('.');//判断是否为文件
if(nPos>0)
{
//获取文件图标
SHGetFileInfo(csName,FILE_ATTRIBUTE_NORMAL,&shInfo,sizeof(shInfo),
SHGFI_ICON|SHGFI_SMALLICON|SHGFI_USEFILEATTRIBUTES);
}
else//参数表示一个目录
{
charchPath[MAX_PATH]={0};
GetCurrentDirectory(MAX_PATH,chPath);//获取当前目录
SHGetFileInfo(chPath,FILE_ATTRIBUTE_NORMAL,&shInfo,sizeof(shInfo),
SHGFI_ICON|SHGFI_SMALLICON);//获取目录图标
}
nPos=shInfo.iIcon;
returnshInfo.hIcon;//返回获取的图标
}
(9)向CSortListCtrl类中添加DisplayPath方法,列举本地或FTP服务器目录和文件信息。
该方法的作用非常重要,在程序的多处地方都需要调用DisplayPath方法,例如,在视图列表中显示本地系统中的某一个目录下的子目录和文件,用户按“BackSpace”键返回上一级目录等,这些都需要调用DisplayPath方法来实现。
DisplayPath方法代码较多,但是并不复杂。
首先根据成员变量m_nListType来判断显示的是本地系统目录还是FTP服务器上的目录。
以显示本地系统目录为例,使用CFileFind类来遍历当前目录的直接子目录和文件,如果是文件,则读取文件的修改日期和大小信息,如果是目录,则标记为文件夹,将这些信息添加到列表视图中,然后获取文件关联的图标索引,设置视图项显示的图像索引,最后为了区分列表视图中的项目表示的是文件还是目录,为每个视图项设置一个额外的整数值,0表示文件,1表示目录。
voidCSortListCtrl:
:
DisplayPath(LPCTSTRlpPath,CFtpConnection*pTemp)
{
DeleteAllItems();//删除所有视图项
if(m_nListType==0)//显示本地系统信息
{
BOOLbFind;//记录查找结果
CFileFindflFind;//定义文件查找对象
CStringcsPath=lpPath;//记录参数信息
m_CurDir=lpPath;//设置当前目录,进入子目录或返回上一级目录时需要依据当前目录
if(csPath.Right
(1)!
="\\")//保证目录以\结尾
{
csPath+="\\";
m_CurDir+="\\";
}
csPath+="*.*";//在目录结尾添加*.*用于查找文件
bFind=flFind.FindFile(csPath);//开始查找文件
CStringcsText,csFileSize,csDataTime;
while(bFind)//设置列表当前显示的目录
{
bFind=flFind.FindNextFi