开发环境
VS2005 SP1,BOOST 1.36, STL, MarkUp
Snap.h
#pragma once
class CSnap
{
public:
CSnap(void);
~CSnap(void);
// 制作快照,并保存为 xml 文件
void SaveSnap( const char* szFileName );
private:
char* m_pszCurDir; //当前路径
};
Snap.cpp
#include <iostream>
#include <Windows.h>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/crc.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem.hpp>
#include "Snap.h"
#include "Markup.h"
using namespace std;
using namespace boost;
namespace fs = boost::filesystem;
const char* g_FolderName = "Update//";
CSnap::CSnap(void)
{
//默认路径是程序 .exe 所在的路径
m_pszCurDir = new char[ MAX_PATH ];
::GetCurrentDirectory( MAX_PATH, m_pszCurDir );
int nLen = strlen( m_pszCurDir );
m_pszCurDir[ nLen ] = '//';
m_pszCurDir[ nLen + 1 ] = '/0';
}
CSnap::~CSnap(void)
{
delete [] m_pszCurDir;
}
// 制作快照,并保存为 xml 文件
void CSnap::SaveSnap( const char* szFileName )
{
char szPath[ MAX_PATH ];
::GetModuleFileName( NULL, szPath, MAX_PATH );
std::string strFullSnapPath = std::string( m_pszCurDir ) + szFileName;
CMarkup xml;
xml.AddElem( "ourgame" );
xml.IntoElem();
std::string strNodeName = "file";
int nIndex = 1;
std::string strSnapPath = std::string( m_pszCurDir ) + std::string( g_FolderName );
fs::path fsPath( strSnapPath ,fs::native );
if ( !fs::exists( fsPath ) )
{
return;
}
fs::recursive_directory_iterator beg_iter(fsPath);
fs::recursive_directory_iterator end_iter;
for (; beg_iter != end_iter; ++beg_iter)
{
if (fs::is_directory(*beg_iter))
{
continue;
}
else
{
uintmax_t filesize = fs::file_size(*beg_iter);
std::string strFullPathFile = beg_iter->path().file_string();
//std::cout << strFullPathFile << endl;
// .exe 对自己和 快照文件 不做快照
//if ( ( strFullPathFile == szPath ) || ( strFullPathFile == strFullSnapPath ) )
//{
// //std::cout << strFullPathFile << endl;
// continue;
//}
char* pszBuf = new char[ filesize ];
memset(pszBuf, 0, filesize);
std::locale prev_loc = std::locale::global( std::locale("chs") ); // 没有这一句的话,文件打开失败
ifstream file;
file.open( strFullPathFile.c_str(),ios_base::in | ios_base::binary );
if(file.is_open())
{
file.read(pszBuf,filesize);
file.close();
boost::crc_32_type ResultCrc32;
//计算文件的CRC值
ResultCrc32.process_block( pszBuf, pszBuf + filesize*sizeof(char) );
unsigned int nCrc32 = ResultCrc32.checksum();
std::locale::global( prev_loc ); // 没有这一句的话,文件中的中文无法输出,且wcout输出中文也失败
//cout << strFullPathFile << endl;
file.close();
std::string strCurrentNodeName = strNodeName + boost::lexical_cast< std::string >( nIndex++ );
std::string strFile = boost::replace_first_copy( strFullPathFile, m_pszCurDir, "" );
//std::cout << strFullPathFile << std::endl;
//std::cout << m_pszCurDir << std::endl;
//std::cout << strFile << std::endl;
xml.AddElem( strCurrentNodeName );
xml.IntoElem();
xml.AddElem( "name", strFile );
xml.AddElem( "checkcode", boost::lexical_cast< std::string >( nCrc32 ) );
xml.OutOfElem();
}
else
{
file.close();
}
delete [] pszBuf;
}
}
xml.Save( szFileName );
}
测试程序
#include <iostream>
#include "Snap.h"
//#include <Windows.h>
using namespace std;
void main(void)
{
CSnap snap;
snap.SaveSnap( "1.xml" );
std::system("pause");
return;
}
将上面的代码编译成 exe,然后在 exe 的目录,新建一个 "Update" 文件夹,并在其中拷贝若干文件,然后运行 exe 程序,会生成一个 "1.xml" 的文件;里面使用 XML 格式记录了 Update 文件夹下面所有的文件信息(文件名,CRC32码)
假如现在有2个 XML 文件,里面分别记录了服务端上 Update 文件夹 和 客户端 Update 文件夹下面的文件信息,现在要对比出差异文件,可以使用下面的函数
//快速检查某个文件或者目录是否存在
BOOL FileExists(LPCTSTR lpszFileName, BOOL bIsDirCheck)
{
//试图取得文件属性
DWORD dwAttributes = GetFileAttributes(lpszFileName);
if(dwAttributes == 0xFFFFFFFF)
return FALSE;
if((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
{
if(bIsDirCheck)
return TRUE;
else
return FALSE;
}
else
{
if(!bIsDirCheck)
return TRUE;
else
return FALSE;
}
}
bool GetSnapInfo( const char* szSnap, std::map< std::string, std::string >& mapFileName_CheckCode )
{
if ( NULL == szSnap )
{
return false;
}
if ( FALSE == FileExists( szSnap, FALSE ) )
{
return false;
}
mapFileName_CheckCode.clear();
CMarkup xml;
xml.Load( szSnap );
std::string strNodeName = "file";
int nIndex = 1;
xml.IntoElem();
std::string strCurrentNodeName = strNodeName + boost::lexical_cast< std::string >( nIndex++ );
while ( xml.FindChildElem( strCurrentNodeName ) )
{
xml.IntoElem();
xml.FindChildElem( "name" );
std::string strFileName = xml.GetChildData();
//cout << strFileName << endl;
xml.FindChildElem( "checkcode" );
std::string strCheckCode = xml.GetChildData();
//cout << strCheckCode << endl;
mapFileName_CheckCode[ strFileName ] = strCheckCode;
xml.OutOfElem();
strCurrentNodeName = strNodeName + boost::lexical_cast< std::string >( nIndex++ );
}
return true;
}
// szSrvSnap 和 szSnap2 表示两个快照文件的全路径
// vecSnap 存放差异文件信息
void GetDiffFile( const char* szSrvSnap, const char* szCliSnap, std::vector< std::string >& vecSnap )
{
if ( ( NULL == szSrvSnap ) || ( NULL == szCliSnap ) )
{
return;
}
if ( ( FALSE == FileExists( szSrvSnap, FALSE ) ) || ( FALSE == FileExists( szCliSnap, FALSE ) ) )
{
return;
}
std::map< std::string, std::string > mapSrvFileName_CheckCode;
std::map< std::string, std::string > mapCliFileName_CheckCode;
GetSnapInfo( szSrvSnap, mapSrvFileName_CheckCode );
GetSnapInfo( szCliSnap, mapCliFileName_CheckCode );
//cout << mapSrvFileName_CheckCode.size() << endl;
//cout << mapCliFileName_CheckCode.size() << endl;
vecSnap.clear();
std::map< std::string, std::string >::iterator iterSrv = mapSrvFileName_CheckCode.begin();
for ( ; iterSrv!=mapSrvFileName_CheckCode.end(); ++iterSrv )
{
std::map< std::string, std::string >::iterator iterCli = mapCliFileName_CheckCode.find( iterSrv->first );
//找到了
if ( iterCli != mapCliFileName_CheckCode.end() )
{
//对比 CRC32 码
if ( iterSrv->second != iterCli->second )
{
vecSnap.push_back( iterSrv->first );
}
}
//没找到
else
{
vecSnap.push_back( iterSrv->first );
}
}
}
鄙人较懒,没有考虑太多,上面的代码实现了基本的快照对比功能;关于程序性能和功能,没有考虑,路过的朋友如果有更好的方法,麻烦贴上来给我学习用;祝大家身体健康