简易文件夹打包程序的设计与实现
摘要:
所谓文件夹打包程序就是指将一个文件夹的内容写入到一个文件之中。本文讨论在win32平台下使用FindFirstFile与FindNextFile函数对文件夹进行深度优先的方法实现简易的文件夹打包程序。
关键字: 文件夹打包 深度优先 win32平台
1引言:
文件夹打包程序被广泛的运用于压缩和网络传输之中,比如说,当我们写电子邮件的时候常用到的发送附件功能,它一次只能发送一个文件,但当需要发送一个文件夹内的所有内容时,一个一个的手动添加文件就显得很不方便,我们通常的做法是将一个文件夹打包生成 一个单一文件再发送。所谓文件夹打包程序就是指将一个文件夹的内容写入到一个文件之中。本文讨论在win32平台下的简易文件夹打包程序。
2原理
要把一个文件夹打包到一个文件中就是要把文件夹内的所有文件与子文件夹内容写入到一个文件中,并且还需要在这个文件中保存下文件夹的目录结构,才能使打包文件可以还原为文件夹。
一般要将多个文件的内容写入到一个文件中并不困难,但要在这个文件中同时将目录的结构保存下来就需要精心的设计。本文介绍一方法是以深度优先的一种遍历方法,先列出所有的目录,再是当前目录的所有文件,然后对列出的目录以这种方式递归的访问。以右图的一个目录结构为例,我们的访问次序为:testdir ->tools -> license.txt -> news.txt -> python.ext ->pythonw.ext -> README.txt -> w9xpopen.exe -> il8n ->versioncheck -> webchecker -> makelocalealias.py -> msgfnt.py -> pygettext.py -> checkversions.py -> pyversioncheck.py -> README.txt -> _checkversion.py
3设计实现
首先是对一个数据结构和两个WIN32函数的说明:
WIN32_FIND_DATA结构描述了一个用FindFirstFile, FindFirstFileEx, 或FindNextFile函数找到的文件或文件平信息。
FindFirstFile函数用于在一个子文件夹中查找一个特定的文件(可以使用通配符)。
FindNextFile函数用于在FindFirstFile之后继续在子文件夹中特定的文件。[1]
创建包函数:
bool CreateTar(string spath,CFile &tarfile){
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
list<WIN32_FIND_DATA> ldirs;
list<WIN32_FIND_DATA> lfiles;
CFile wfile;
string stype;
if(wfile.Open(spath.c_str(), CFile::modeRead, NULL) ) {
wfile.Close();
int pos = spath.rfind("//");
stype = spath.substr( pos+1 );
spath = spath.substr(0, spath.rfind("//") );
}else{
stype = "*.*";
}
SetCurrentDirectory(spath.c_str() );
hFind = FindFirstFile(stype.c_str(), &FindFileData);
while(hFind != INVALID_HANDLE_VALUE){
if( FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes ){
// FILE_ATTRIBUTE_DIRECTORY说明是一个文件夹
if( strcmp( FindFileData.cFileName, "." ) &&
strcmp( FindFileData.cFileName, ".." ) )
ldirs.push_back(FindFileData);
}else{
lfiles.push_back(FindFileData);
}
if(!FindNextFile(hFind, &FindFileData))
break;
}
FindClose(hFind);
//写入当前目录中的子目录信息
int ndirs = ldirs.size();
int nfiles = lfiles.size();
tarfile.Write( (char*)&ndirs, sizeof(int) );
tarfile.Write( (char*)&nfiles, sizeof(int) );
if(0==ndirs && 0==nfiles){
SetCurrentDirectory(".." );
return true;
}
list<WIN32_FIND_DATA>::iterator itr = ldirs.begin();
while(itr != ldirs.end() ){
int fnlen = strlen( itr->cFileName );
char* ccurr_file_name = itr->cFileName ;
tarfile.Write( (char*)&fnlen, sizeof(int) ); //file name length
tarfile.Write( itr->cFileName, fnlen); //file name string
CreateTar( itr->cFileName ,tarfile);
++itr;
}
//将当前文件夹中的文件写入目录包文件中
const int bflen = 100;
char pbytes[bflen];
list<WIN32_FIND_DATA>::iterator itr2 = lfiles.begin();
while(itr2 != lfiles.end() ){
CFile tempfile( itr2->cFileName, CFile::modeRead );
int templen = strlen( itr2->cFileName );
tarfile.Write( (char*)&templen, sizeof(int) );
tarfile.Write( (char*)&itr2->cFileName, templen);
//写入文件
int nowflen = tempfile.GetLength();
tarfile.Write( (char*)&nowflen, sizeof(int) );
for(int j=0; j<nowflen/bflen; ++j) {
tempfile.Read( pbytes, bflen );
tarfile.Write( pbytes, bflen );
}
tempfile.Read( pbytes, nowflen % bflen );
tarfile.Write( pbytes, nowflen % bflen );
tempfile.Close();
++itr2;
}
SetCurrentDirectory(".." ); //回到止层目录
return true;
}
解包:
bool ExtraTar(CFile &srcfile, string spath){
SetCurrentDirectory(spath.c_str() );
int ndirs,nfiles;
srcfile.Read( (char*)&ndirs, sizeof(int) );
srcfile.Read( (char*)&nfiles, sizeof(int) );
if( 0==ndirs && 0==nfiles )
return true;
int i,nfnlen;
//create folders
char chname[255];
for(i=0; i<ndirs; ++i) {
srcfile.Read( (char*)&nfnlen, sizeof(int) );
srcfile.Read( chname, nfnlen );
chname[nfnlen] = 0;
SECURITY_ATTRIBUTES sAttr = {0, 0, 0};
CreateDirectory( chname, &sAttr);
ExtraTar(srcfile, chname );
}
const int bflen = 100;
char cfbuffer[bflen];
for(i=0; i<nfiles; ++i) {
srcfile.Read( (char*)&nfnlen, sizeof(int) );
srcfile.Read( chname, nfnlen );
chname[nfnlen] = 0;
int nowflen;
srcfile.Read( (char*)&nowflen, sizeof(int) );
CFile tarfile( chname, CFile::modeWrite | CFile::modeCreate );
for(int j=0; j<nowflen/bflen; ++j) {
srcfile.Read( cfbuffer, bflen );
tarfile.Write( cfbuffer, bflen );
}
srcfile.Read( cfbuffer, nowflen % bflen );
tarfile.Write( cfbuffer, nowflen % bflen );
tarfile.Close();
}
SetCurrentDirectory("..");
return 0;
}
4结语
本文实现了一个简易的文件夹打包程序,之所以说它简单是因为:在包文件中它只保存了文件夹的文件名,而对文件与只保存了文件名和文件大小的信息,没有保存它更多的文件属性,如是创建日期、最后一次修改上期、是否共享等。但要保存这些信息并不困难因为在WIN32_FIND_DATA结构中这些信息都已经存在,在写入的时候写入WIN32_FIND_DATA结构中更多的信息就可以了,同时还在要在文件与文件夹的创建时将这些信息还原就可以了。虽然这个程序还有一些不足之处,但已经具有一写的实用性(我在自己的一个压缩程序中就用到过上述算法),给出了打包程序的一种简洁思路。
参考文献:
[1] Microsoft MSDN 20001
[2] 候捷 《深入浅出MFC》 华中科技大学