c++实现移动文件和目录带恢复功能

85 篇文章 4 订阅

之前写过一篇《c++实现移动文件夹到指定文件夹》,最近正好又需要这样的功能,而且还增加了一个需求,希望能够记录移动的具体内容,并且能够根据记录实现恢复功能,这个实现起来没什么难度,唯一需要注意的是,记录需要加锁,并且考虑到程序崩溃等问题,需要将每次成功或者失败记录即时写入文件,而不能在程序结束时写入,否则程序一旦崩溃,所有的移动记录将丢失。

于是我写了一个类,实现了文件和目录的移动,纯c++实现,可以记录移动过程,也有恢复功能呢,可以指定记录的文件名,默认为"restore.log"。全部使用静态函数实现,可以直接调用。

//author:autumoon
//联系邮箱:9506#163.com
//日期:2023-03-24 

#pragma once
#include <string>
#include <vector>
#include <mutex>
#include <tchar.h>

//移动文件或者目录,并带有恢复功能,内部自动判断是文件还是目录
#ifdef _UNICODE

#include <string>

#ifndef _tstring
#define _tstring std::wstring
#endif // _tstring

#else

#ifndef _tstring
#define _tstring std::string
#endif // _tstring

#endif

#if _MSC_VER >=1900 // VC2015或者以上
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif

//nMode 0记录移动信息 记录错误信息 -1仅仅记录错误信息 -2无任何记录

class CMoveItemsLog
{
public:
	CMoveItemsLog() {};
	~CMoveItemsLog() {};

public:
	static bool CleanHistoryLog();
	static bool MoveOneItem(const _tstring& stSrcPath, const _tstring& stDstPath, bool bFailIfExists = false);
	static bool MoveItems(const std::vector<_tstring>& lSrcPaths, const std::vector<_tstring>& lDstPaths, bool bFailIfExists = false);
	static bool RestoreItemsByLog(const _tstring& stLogPath = _T("restore.log"));

	//只存储移动记录,可用于外部调用
	static void LogItemMove(const _tstring& stSrcPath, const _tstring& stDstPath);

	//默认无须调用
	static void SetLogFilePath(const _tstring& stLogPath = _T("restore.log")) { m_stLogFilePath = stLogPath;}
	static void SetLogMode(int nMode = 0){m_nMode = nMode;}

private:
	static bool moveOneFile(const _tstring& stSrcPath, const _tstring& stDstPath, bool bFailIfExists = false);
	static bool moveOneDirectory(const _tstring& stSrcPath, const _tstring& stDstPath, bool bFailIfExists = false);

	static void logErrItemMove(const _tstring& stSrcPath, const _tstring& stDstPath);

private:
	static std::recursive_mutex m_mutexLog;
	static std::recursive_mutex m_mutexErrLog;

	static int m_nMode;
	static _tstring m_stLogFilePath;
	static _tstring m_stErrLogFilePath;
	
};

实现文件:

//author:autumoon
//联系邮箱:9506#163.com
//日期:2023-03-24 
#include "MoveItemsLog.h"
#include "StdStrFile.h"

int CMoveItemsLog::m_nMode = 0;
_tstring CMoveItemsLog::m_stLogFilePath = _T("restore.log");
_tstring CMoveItemsLog::m_stErrLogFilePath = _T("moveErr.log");

std::recursive_mutex CMoveItemsLog::m_mutexLog;
std::recursive_mutex CMoveItemsLog::m_mutexErrLog;

inline bool isSpecialDir(const char* path)
{
	return strcmp(path, "..") == 0 || strcmp(path, ".") == 0;
}

bool CMoveItemsLog::CleanHistoryLog()
{
	if (CStdFile::IfAccessFile(m_stLogFilePath) && CStdFile::RemoveFile(m_stLogFilePath.c_str()) != 0)
	{
		return false;
	}

	if (CStdFile::IfAccessFile(m_stErrLogFilePath))
	{
		CStdFile::RemoveFile(m_stErrLogFilePath);
	}

	return true;
}

void CMoveItemsLog::LogItemMove(const _tstring& stSrcPath, const _tstring& stDstPath)
{
	m_mutexLog.lock();

	switch (m_nMode)
	{
	case 0:
		CStdFile::SaveTXTLine(m_stLogFilePath, stSrcPath + _T("\n"), true);
		CStdFile::SaveTXTLine(m_stLogFilePath, stDstPath + _T("\n"), true);
		break;
	case -1:
		break;
	case -2:
		break;
	default:
		break;
	}

	m_mutexLog.unlock();
}

bool CMoveItemsLog::MoveOneItem(const _tstring& stSrcPath, const _tstring& stDstPath, bool bFailIfExists)
{
	if (CStdFile::IfAccessFile(stSrcPath))
	{
		return moveOneFile(stSrcPath, stDstPath);
	}
	else if (CStdDir::IfAccessDir(stSrcPath))
	{
		if (moveOneDirectory(stSrcPath, stDstPath, bFailIfExists))
		{
			LogItemMove(stSrcPath, stDstPath);
			return true;
		}
		else
		{
			logErrItemMove(stSrcPath, stDstPath);
			return false;
		}
	}

	return false;
}

bool CMoveItemsLog::MoveItems(const std::vector<_tstring>& lSrcPaths, const std::vector<_tstring>& lDstPaths, bool bFailIfExists)
{
	bool bRet = true;

	size_t nItemCount = std::min(lSrcPaths.size(), lDstPaths.size());

	for (size_t i = 0; i < nItemCount; ++i)
	{
		const _tstring& stSrcPath = lSrcPaths[i];
		const _tstring& stDstPath = lDstPaths[i];

		if (!MoveOneItem(stSrcPath, stDstPath, bFailIfExists))
		{
			bRet = false;
		}
	}

	return bRet;
}

bool CMoveItemsLog::RestoreItemsByLog(const _tstring& stLogPath)
{
	std::vector<_tstring> vContentInFile;
	int nFileCount = static_cast<int>(CStdFile::ParseTXTFile(stLogPath, vContentInFile) >> 1);

	bool bRet = true;

	//注意此时不能使用size_t 因为有边界负值判断
	for (int i = nFileCount - 1; i >= 0 ; --i)
	{
		const _tstring& stCurItem = vContentInFile[i * 2 + 1];
		const _tstring& stCurDstItem = vContentInFile[i * 2];

		if (!MoveOneItem(stCurItem, stCurDstItem, false))
		{
			bRet = false;
		}
	}

	if (bRet)
	{
		//恢复成功时删除记录文件
		CStdFile::RemoveFile(m_stLogFilePath);
	}

	return bRet;
}

bool CMoveItemsLog::moveOneFile(const _tstring& stSrcPath, const _tstring& stDstPath, bool bFailIfExists)
{
	if (bFailIfExists && CStdFile::IfAccessFile(stDstPath))
	{
		logErrItemMove(stSrcPath, stDstPath);
		return false;
	}

	_tstring strDirPath = CStdStr::GetDirOfFile(stDstPath);
	if (!CStdDir::IfAccessDir(strDirPath) && !CStdDir::CreateDir(strDirPath))
	{
		logErrItemMove(stSrcPath, stDstPath);
		return false;
	}

	if (CStdFile::RenameFile(stSrcPath.c_str(), stDstPath.c_str()) != 0)
	{
		logErrItemMove(stSrcPath, stDstPath);
		return false;
	}

	LogItemMove(stSrcPath, stDstPath);

	return true;
}

bool CMoveItemsLog::moveOneDirectory(const _tstring& srcPath, const _tstring& dstPath, bool bFailIfExists)
{
#ifdef _UNICODE
	std::string pathBak = CStdStr::ws2s(CStdStr::AddSlashIfNeeded(srcPath));
#else
	std::string pathBak = CStdStr::AddSlashIfNeeded(srcPath);
#endif // _UNICODE
	_finddata_t fileinfo;
	intptr_t hFile;
	std::string p;
	if ((hFile = _findfirst(p.assign(pathBak).append("*.*").c_str(), &fileinfo)) != -1)
	{
		while (_findnext(hFile, &fileinfo) == 0)
		{
			if (isSpecialDir(fileinfo.name))
				continue;
			std::string sCurDirFile = p.assign(pathBak).append(fileinfo.name);
			if (fileinfo.attrib & _A_SUBDIR)
			{
				_tstring stSubDstPath = CStdStr::AddSlashIfNeeded(dstPath) + CStdStr::GetNameOfDir(sCurDirFile);
				//如果是目录,开始递归删除目录中的内容
#ifdef _UNICODE
				_tstring stCurDirFile = CStdStr::s2ws(sCurDirFile);
				moveOneDirectory(stCurDirFile, stSubDstPath);
#else
				moveOneDirectory(sCurDirFile, stSubDstPath);
#endif // _UNICODE
			}
			else
			{
#ifdef _UNICODE
				_tstring stPathBak = CStdStr::s2ws(pathBak);
				_tstring stCurDirFile = CStdStr::s2ws(sCurDirFile);
#else
				_tstring stPathBak = pathBak;
				_tstring stCurDirFile = sCurDirFile;
#endif
				_tstring stSubDstFile = CStdStr::AddSlashIfNeeded(dstPath) + CStdStr::GetNameOfFile(stCurDirFile);
				_tstring stSubDir = CStdStr::GetDirOfFile(stSubDstFile);
				if (!CStdDir::IfAccessDir(stSubDir) && !CStdDir::CreateDir(stSubDir))
				{
					continue;
				}

				if (CStdFile::RenameFile(stCurDirFile, stSubDstFile) != 0)
				{
					//出现错误时终止移动
					_findclose(hFile);
					return false;
				}
			}
		}
		_findclose(hFile);//关闭打开的文件句柄,并释放关联资源,否则无法删除空目录
	}

	//删除空目录,必须在递归返回前调用_findclose,否则无法删除目录
	if (_rmdir(pathBak.c_str()) == -1)
	{
		logErrItemMove(srcPath, dstPath);
		return false;
	}

	return true;
}

void CMoveItemsLog::logErrItemMove(const _tstring& stSrcPath, const _tstring& stDstPath)
{
	m_mutexErrLog.lock();

	switch (m_nMode)
	{
	case 0:
	case -1:
		CStdFile::SaveTXTLine(m_stErrLogFilePath, stSrcPath + _T("\n"), true);
		CStdFile::SaveTXTLine(m_stErrLogFilePath, stDstPath + _T("\n"), true);
		break;
	case -2:
		break;
	default:
		break;
	}

	m_mutexErrLog.unlock();
}

注意,部分函数需要另外实现,实现起来也不难,欢迎交流与讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值