【项目】云备份

云备份

  • 云备份
    • 概述
      • 框架
    • 功能演示
      • 服务端
      • 客户端
    • 公共模块
      • 文件操作模块
      • 目录操作模块
    • 服务端模块
      • 功能划分
      • 功能细分模块
        • 数据管理
        • 热点管理
    • 客户端模块
      • 功能划分
      • 功能细分模块
        • 数据管理
        • 目录检查
        • 文件备份

云备份

概述

自动将本地计算机上指定文件夹中需要备份的文件上传备份到服务器中。并且能够随时通过浏览器进行查看并且下载,其中下载过程支持断点续传功能,而服务器也会对上传文件进行热点管理,将非热点文件进行压缩存储,节省磁盘空间。

框架

实现客户端和服务端双端程序,合作实现总体的自动云备份功能

本项目有三大模块:

  • 客户端模块
  • 服务端模块
  • 公共模块

除了自己实现的模块之外,还引入了三方库作为辅助:

  • Jsoncpp进行序列化辅助
  • Httplib进行网络传输辅助
  • c++17–filestream进行目录检测辅助

功能演示

服务端

  • 若客户端有文件上传,则会自动下载到服务端的backupDir目录下

    在这里插入图片描述

  • 若此文件超过30s时间没有被获取则会自动压缩到packDir目录下

    在这里插入图片描述

  • 从浏览器访问备份呢列表

    在这里插入图片描述

  • 点击文件名下载文件:

    在这里插入图片描述

    当服务端接收到下载命令时,若此文件已在压缩目录下,则先将其解压到备份目录中,再将内容发送给客户端

  • 若此时有用户访问了服务端并下载了某个文件,下载一半时,服务端异常关闭导致用户下载中断,服务端恢复后,用户能继续正常的从上次中断的地方继续下载, 实现断点续传

    在这里插入图片描述

客户端

  • 找到backup目录,若没有时则创建(可以考虑写成读取配置文件)
    在这里插入图片描述

    第一次载入时,若没有此目录会自动创建

  • 不断轮询检测目录backup,一旦有文件新增或更新,就会发送到远端,实现云备份

    在这里插入图片描述

  • 注意,不一定所有更新都会发送到远端,若有一份很大的文件正在拷贝进backup目录,相当于这份文件在实时更新,但实际是此文件还没有拷贝完毕,不能发送到远端,因此客户端的策略是对某个更新的文件,若此文件上一次更新时间与当前时间相比超过了3s钟才进行云备份

    在这里插入图片描述

公共模块

公共模块是完成一系列对文件或者目录上的操作,本项目中细分为文件操作模块和目录操作模块,以及日志等简单的公共模块

文件操作模块

namespace cloud
{//example: you can nest your own log systemvoid LOG(const char* str){printf("[%s |%d]: %s\n", __FILE__, __LINE__, str);}//for operationnamespace fs = std::experimental::filesystem;class FileUtil{private:std::string _fileName;public:FileUtil(const std::string& fileName) :_fileName(fileName) {}std::string FileName(){return fs::path(_fileName).filename().string();}time_t LastMTime(){struct stat st;if (stat(_fileName.c_str(), &st) < 0){LOG("file error");return -1;}return st.st_mtime;}size_t FileSize() noexcept{struct stat st;if (stat(_fileName.c_str(), &st) < 0){LOG("get file size error");return 0;}return st.st_size;}bool GetContentByPosLen(std::string* body, size_t pos, size_t len){size_t fsize = FileSize();if (pos + len > fsize){LOG("file len error");return false;}std::ifstream ifs;ifs.open(_fileName, std::ios::binary);if (!ifs.is_open()){LOG("open failed");return false;}ifs.seekg(pos, std::ios::beg);body->resize(len);ifs.read(&(*body)[0], len);if (!ifs.good()){LOG("read failed");ifs.close();return false;}ifs.close();return true;}bool SetContent(const std::string& body){std::ofstream ofs;ofs.open(_fileName, std::ios::binary);if (!ofs.is_open()){LOG("open file error");return false;}ofs.write(&body[0], body.size());if (!ofs.good()){LOG("write error");ofs.close();return false;}ofs.close();return true;}};// end FileUtil
}// end namespace 

目录操作模块

class DirUtil{private:std::string _dirPath;public:explicit DirUtil(const std::string& dirName) :_dirPath(dirName) {}bool Exists(){return fs::exists(_dirPath);}bool ScanDirectory(std::vector<std::string>* fileArray){if (!Exists()){return fs::create_directories(_dirPath);}for (auto& file : fs::directory_iterator(_dirPath)){//to do, directory continue temporarilyif (fs::is_directory(file))continue;fileArray->push_back(fs::path(file).relative_path().string());}return true;}};//end DirUtil

服务端模块

功能划分

  • 针对客户端上传的文件进行备份存储

  • 能够对文件进行热点文件管理,对非热点文件进行压缩存储,节省磁盘空间。

  • 支持客户端浏览器查看访问文件列表。

  • 支持客户端浏览器下载文件,并且下载支持断点续传。

  • 备份信息持久化存储和状态恢复

功能细分模块

数据管理

数据管理模块需要完成配置文件的加载以及数据的管理维护工作,因此大部分设计都集中在这一块,简要概括以下几点:

  • 配置文件的加载单独划分为一个模块,并设计为单例模式,由数据管理者启动时加载并维护

    配置文件需要有以下信息,做到服务端功能灵活使用

    • 热点判断时间
    • 服务器监听窗口
    • 下载的url前缀路径
    • 压缩包后缀名称
    • 备份文件存放目录
    • 压缩包存放目录
    • 服务器ip地址
    • 数据信息持久化存储文件带路径名称

    Config类:

    • namespace cloud {
      #define CONFIG_FILE "./cloud.conf"class Config{private:Config(){ReadConfigFile();}static Config *_instance;static std::mutex _mutex;private:int _hot_time;int _server_port;std::string _server_ip;std::string _download_prefix;std::string _packfile_suffix;std::string _pack_dir;std::string _back_dir;std::string _backup_file;bool ReadConfigFile() {FileUtil fu(CONFIG_FILE);std::string body;if(fu.GetContent(&body) == false){std::cout << "load config file failed!\n";return false;}Json::Value root;if (JsonUtil::UnSerialize(body, &root) == false){std::cout << "parse config file failed!\n";return false;}_hot_time = root["hot_time"].asInt();_server_port = root["server_port"].asInt();_server_ip = root["server_ip"].asString();_download_prefix = root["download_prefix"].asString();_packfile_suffix = root["packfile_suffix"].asString();_pack_dir = root["pack_dir"].asString();_back_dir = root["back_dir"].asString();_backup_file = root["backup_file"].asString();return true;}public:static Config *GetInstance() {if (_instance == NULL){_mutex.lock();if (_instance == NULL) {_instance = new Config();}_mutex.unlock();}return _instance;}int GetHotTime() {return _hot_time;}int GetServerPort() {return _server_port;}std::string GetServerIp() {return _server_ip;}std::string GetDownloadPrefix() {return _download_prefix;}std::string GetPackFileSuffix() {return _packfile_suffix;}std::string GetPackDir() {return _pack_dir;}std::string GetBackDir() {return _back_dir;}std::string GetBackupFile() {return _backup_file;}};Config *Config::_instance = NULL;std::mutex Config::_mutex;
      }#endif

      易错点:类内静态成员变量需要在类外初始化

  • Jsoncpp

服务器启动时,需要根据配置文件中的备份文件将上一次退出时服务器的备份信息状态加载进本次服务的启动中恢复状态,因此需要将备份数据的信息管理起来,但由于服务端对数据需要的管理较多,若单纯的使用字符串分割符切割存储肯定是不行的,因此使用Json序列化的方式,将数据管理信息序列化后再存储进备份信息文件中,启动服务器时,根据配置文件找到备份信息文件并通过反序列化加载数据管理信息模块进行状态恢复。

配置文件信息也属于数据管理模块的范畴,只不过重新封装一个解耦和,也是通过Json对配置文件信息进行反序列化,而配置文件需要用特定的格式完成编写以便序列化加载配置信息

使用Json

  • 使用命令安装,环境:CentOS Linux release 7.9.2009 (Core)

    sudo yum install epel-release
    sudo yum install jsoncpp-devel
    

    安装完后,在系统头文件路径下就会有json需要使用的头文件(自动放的)

    在这里插入图片描述

  • 使用时,只需要包含此头文件的系统路径即可

    //example
    #include <iostream>
    #include <string>
    #include <memory>
    #include <fstream>
    #include <jsoncpp/json/value.h>
    #include <jsoncpp/json/json.h>void TestJsonCpp()
    {std::string str = R"({"name":"nicky", "age":18, "score":[50,80,90]})";Json::Value root;Json::CharReaderBuilder builder;std::unique_ptr<Json::CharReader> reader(builder.newCharReader());std::string err;reader->parse(str.c_str(), str.c_str() + str.size(), &root, &err);std::cout << root["name"].asCString() << std::endl;std::cout << root["age"].asInt() << std::endl;int sz = root["score"].size();for (int i = 0; i < sz; ++i){std::cout << root["score"][i].asFloat() << std::endl; }
    }int main()
    {TestJsonCpp();return 0;
    }
    
    • 注意编译时需要链接上jsoncpp的静态库:

      g++ xxx.cpp -o xxx -ljsoncpp
      

      在这里插入图片描述

    JsonUtil类

    • class JsonUtil{
      public:static bool Serialize(const Json::Value &root, std::string *str){Json::StreamWriterBuilder swb;std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss; if (sw->write(root, &ss) != 0) {std::cout << "json write failed!\n";return false;}*str = ss.str();return true;}static bool UnSerialize(const std::string &str, Json::Value *root){Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err;bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);if (ret == false) {std::cout << "parse error: " << err << std::endl;return false; }return true;}
      };
      
  • DataManager

    客户端使用简单的映射来建立数据信息管理的唯一性–<file, file-fsize-fmtime>,而由于服务端还需要管理上传文件的压缩包路径和文件备份路径,因此我们选择将文件的信息单独分出来一个模块,用来管理文件的备份信息–BackupInfo,而DataManager中的映射表关系就是建立文件名和文件对应的备份信息之间的映射

    namespace cloud
    {class BackupFileInfo{public:bool _pack_flag;size_t _fsize;time_t _mtime;time_t _atime;std::string _urlPath;std::string _realPath;std::string _packPath;BackupFileInfo(){}explicit BackupFileInfo(const std::string &realPath){FileUtil fu(realPath);if (!fu.Exists()){LOG("backup path not exist!");return;}_pack_flag = false;_fsize = fu.FileSize();_mtime = fu.LastMTime();_atime = fu.LastATime();_realPath = realPath;// get from config -- which can be changedConfig *config = Config::GetInstance(); // single instancestd::string packDir = config->GetPackDir();std::string packSuffix = config->GetPackSuffix();std::string downPrefix = config->GetDownPrefix();_packPath = packDir + fu.FileName() + packSuffix; // ./packDir/a.txt.lz_urlPath = downPrefix + fu.FileName();           // /download/a.txt}}; // end BackuoInfoclass DataManager{private:std::string _persistentPath;pthread_rwlock_t _rwLock;std::unordered_map<std::string, cloud::BackupFileInfo> _backupTable;public:DataManager(){_persistentPath = Config::GetInstance()->GetPersistentPath();pthread_rwlock_init(&_rwLock, NULL);InitLoad();}void Insert(BackupFileInfo &info){pthread_rwlock_wrlock(&_rwLock);_backupTable[info._realPath] = info;pthread_rwlock_unlock(&_rwLock);LOG("Insert into table success!");}void InitLoad(){FileUtil fu(_persistentPath);if(!fu.Exists()){LOG("persistent path not exist!");return;}std::string body;fu.GetContentByPosLen(&body, 0, fu.FileSize());//UnserializationJson::Value root;JsonUtil::UnSerialize(body, &root);//insert into tablefor (auto &item : root){BackupFileInfo info;info._atime = item["atime"].asInt64();info._mtime = item["mtime"].asInt64();info._fsize= item["fsize"].asInt64();info._pack_flag = item["packflag"].asBool();info._packPath = item["packPath"].asString();info._realPath = item["realPath"].asString();info._urlPath = item["urlPath"].asString();Insert(info);}LOG("InitLoad success!");}void GetAll(std::vector<BackupFileInfo> *allData){pthread_rwlock_rdlock(&_rwLock);for (auto &item : _backupTable){allData->push_back(item.second);}pthread_rwlock_unlock(&_rwLock);LOG("GetAll success!");}void Storage(){//get all data in tablestd::vector<BackupFileInfo> allData;GetAll(&allData);//add to jsonJson::Value root;for (auto &item : allData){Json::Value value;value["atime"] = (Json::Int64)item._atime;value["mtime"] = (Json::Int64)item._mtime;value["fsize"] = (Json::Int64)item._fsize;value["packflag"] = item._pack_flag;value["packPath"] = item._packPath;value["realPath"] = item._realPath;value["urlPath"] = item._urlPath;root.append(value);}LOG("add to json success!");//serializationstd::string body;JsonUtil::Serialize(root, &body);LOG("serialization success!");//write to fileFileUtil fu(_persistentPath);fu.SetContent(body);LOG("write to file success!");LOG("Storage success!");}~DataManager(){pthread_rwlock_destroy(&_rwLock);Storage();}}; // end DataManager
    } // end namespace#endif
热点管理

对服务器上备份的文件进行检测,哪些文件长时间没有被访问,则认为是非热点文件,则压缩存储,节省磁盘空间。

实现思路:
遍历所有的文件,检测文件的最后一次访问时间,与当前时间进行相减得到差值,这个差值如果大于设定好的非热点判断时间则认为是非热点文件,则进行压缩存放到压缩路径中,删除源文件遍历所有的文件:
1.从数据管理模块中遍历所有的备份文件信息2.遍历备份文件夹,获取所有的文件进行属性获取,最终判断选择第二种:遍历文件夹,每次获取文件的最新数据进行判断,并且还可以解决数据信息缺漏的题
1.遍历备份目录,获取所有文件路径名称
2.逐个文件获取最后一次访问时间与当前系统时间进行比较判断
3.对非热点文件进行压缩处理,删除源文件
4.修改数据管理模块对应的文件信息(压缩标志-》true)

使用bundle

使用第三方模块bundle来对文件进行压缩和解压:

  • 从git克隆bundle库:

    sudo yum install git
    git clone https://github.com/r-lyeh-archived/bundle.git
    
  • 将bundle库中的bundle.h和bundle.cpp拷贝到工程目录下

  • 使用时,包含bundle的头文件并在编译时链接cpp即可:

  • //example:compress:#include <iostream>
    #include <string>
    #include <fstream>
    #include "bundle.h"void TestBundle(int argc, char* argv[])
    {if(argc < 3){std::cout << "Usage: ./cloud <origin path> <bundle name>" << std::endl;return ;}std::string origin_path = argv[1];std::string bundle_name = argv[2];std::ifstream ifs;std::ofstream ofs;std::cout << origin_path << std::endl;std::cout << bundle_name << std::endl;ifs.open(origin_path, std::ios::binary);if(!ifs.is_open()){std::cout << "Open file failed!" << std::endl;return ;}ifs.seekg(0, std::ios::end);size_t fsize = ifs.tellg();ifs.seekg(0, std::ios::beg);std::string body;std::cout << fsize << std::endl;body.resize(fsize);ifs.read(&body[0], fsize);ofs.open(bundle_name, std::ios::binary);if(!ofs.is_open()){std::cout << "Open file failed!" << std::endl;return ;}std::string packed = bundle::pack(bundle::LZIP, body);ofs.write(packed.c_str(), packed.size());ifs.close();ofs.close();
    }int main(int argc, char* argv[])
    {TestBundle(argc, argv);return 0;
    }
    
  • //example : uncompress
    #include <iostream>
    #include <fstream>
    #include "bundle.h"void TestUncompress(int argc, char* argv[])
    {if(argc < 3){std::cout << "Usage: ./uncompress origin_path unpacked" << std::endl;return;}std::ifstream ifs;std::ofstream ofs;ifs.open(argv[1], std::ios::binary);if(!ifs.is_open()){std::cout << "open file failed " << std::endl;return;}std::string body;ifs.seekg(0, std::ios::end);size_t fsize = ifs.tellg();ifs.seekg(0, std::ios::beg);body.resize(fsize);ifs.read(&body[0], fsize);if(!ifs.good()){std::cout << "read failed " <<std::endl;return;}ofs.open(argv[2], std::ios::binary);if(!ifs.is_open()){std::cout << "open failed " << std::endl;return;}std::string unpacked = bundle::unpack(body);ofs.write(&unpacked[0], unpacked.size());ifs.close();ofs.close();}
    int main(int argc, char* argv[])
    {TestUncompress(argc, argv);
    }
  • 编译时,需要带上bundle的源文件并链接上线程库:

    在这里插入图片描述

在这里插入图片描述

  • 尝试压缩某个文件并计算压缩前文件的MD5值

    在这里插入图片描述

  • 尝试解压刚压缩的文件并计算解压后的MD5值

    在这里插入图片描述

  • 比较md5值,发现相同,即文件内容一致,压缩与解压成功

  • HotManager类:

    extern cloud::DataManager* dataManager;

namespace cloud
{
class HotManager
{
private:
int _hotTime;
std::string _packFileDirPath;
std::string _backupFileDirPath;
std::string _packfileSuffix;
public:
HotManager()
{
Config* config = Config::GetInstance();
_packFileDirPath = config->GetPackDir();
_backupFileDirPath = config->GetBackupDir();
_packfileSuffix = config->GetPackSuffix();
_hotTime = config->GetHotTime();

          DirUtil du1(_packFileDirPath);if(!du1.Exists()){if(!du1.CreateDirectory()){LOG("create pack dir failed!");}}DirUtil du2(_backupFileDirPath);if (!du2.Exists()){if (!du2.CreateDirectory()){LOG("create backup dir failed!");}}LOG("hot manager init success!");}bool HotJudge(std::string &file){FileUtil fu(file);time_t lastATime = fu.LastATime();time_t curTime = time(NULL);if(curTime - lastATime > _hotTime){return true;}return false;}void RunModule(){while (true){//scan dir pathDirUtil du(_backupFileDirPath);std::vector<std::string> files;du.ScanDirectory(&files);//check all files in dirfor (auto &file : files){//if hot file, continueif(!HotJudge(file)){LOG("file is not hot, continue!");continue;}//not a hot file, pack up//get backupinfoBackupFileInfo info;if(!dataManager->GetInfoByFilePath(file, &info)){info.NewBackupFileInfo(file);}//use info to compress fileFileUtil fu(file);if(!fu.Compress(info._packPath)){LOG(std::string("compress file failed: " + file).c_str());continue;}//remove filefu.Remove();info._pack_flag = true;dataManager->Update(info);}sleep(1);}}}; // end HotManager

} // end namespace

#endif

#### 业务处理此项目中,客户端能通过浏览器访问服务端ip,服务端基于客户端访问的ip地址来返回相应的页面展示目前已经备份在服务器的文件,并可通过点击文件将备份文件重新下载至本地因此我们需要维护一个模块,用来处理客户端的发送请求,并根据请求做出相应的响应,我们使用到了c++的库httplib来完成HTTP网络通信,此项目中,我们只需维护3个url请求:+ listshow 或者是 ’/’ -- 网页展示+ upload -- 客户端上传请求+ dowload -- 浏览器下载请求![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5C86134%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20240909183712693.png&pos_id=img-liw4TmM1-1725878337219)```c++
static void ListShow(const httplib::Request &req, httplib::Response &res)
{LOG(std::string("receive request: " + req.path).c_str());std::vector<BackupFileInfo> files;dataManager->GetAll(&files);// construct htmlstd::stringstream ss;ss << "<html><head><title>Download</title></head>";ss << "<body><h1>Download</h1><table>";for (auto &file : files){ss << "<tr>";std::string filename = FileUtil(file._realPath).FileName();ss << "<td><a href='" << file._urlPath << "'>" << filename << "</a></td>";ss << "<td align='right'>" << TimeToStr(file._mtime) << "</td>";ss << "<td align='right'>" << file._fsize / 1024 << "k</td>";ss << "</tr>";}ss << "</table></body></html>";res.body = ss.str();res.set_header("Content-Type", "text/html");res.status = 200;LOG(std::string("list show success, file count: " + std::to_string(files.size())).c_str());return;
}static void Upload(const httplib::Request &req, httplib::Response &res)
{auto ret = req.has_file("file");if (!ret){LOG("no file");res.status = 400;return;}const auto &file = req.get_file_value("file");std::string backupFileDirPath = Config::GetInstance()->GetBackupDir();std::string fullPath = backupFileDirPath + FileUtil(file.filename).FileName();FileUtil fu(fullPath);fu.SetContent(file.content);BackupFileInfo info;info.NewBackupFileInfo(fullPath);dataManager->Insert(info);LOG("process upload success");return;
}static std::string TimeToStr(time_t time)
{std::string tmp = std::ctime(&time);return tmp;
}static std::string GetETag(const BackupFileInfo &info)
{// etag : filename-fsize-mtimeFileUtil fu(info._realPath);std::string etag = fu.FileName();etag += "-";etag += std::to_string(info._fsize);etag += "-";etag += std::to_string(info._mtime);return etag;
}static void Download(const httplib::Request &req, httplib::Response &res)
{LOG(std::string("receive request: " + req.path).c_str());BackupFileInfo info;if (!dataManager->GetInfoByUrl(req.path, &info)){LOG("url not found: ");res.status = 400;return;}if (info._pack_flag == true){FileUtil fu(info._packPath);if (!fu.UnCompress(info._realPath)){LOG("uncompress error");res.status = 400;return;}fu.Remove();info._pack_flag = false;dataManager->Update(info);}bool retrans = false;std::string old_etag;if (req.has_header("If-Range")){old_etag = req.get_header_value("If-Range");if (old_etag == GetETag(info)){retrans = true;LOG("retrans open");}}FileUtil fu(info._realPath);if (retrans){std::string range = req.get_header_value("Range");fu.GetContentByPosLen(&res.body, 0, fu.FileSize());res.set_header("Accept-Ranges", "bytes");res.set_header("ETag", GetETag(info));res.set_header("Content-Type", "application/octet-stream");// res.set_header("Content-Range", "bytes start-end/fsize");res.status = 206;}else{fu.GetContentByPosLen(&res.body, 0, fu.FileSize());res.set_header("Accept-Ranges", "bytes");res.set_header("ETag", GetETag(info));res.set_header("Content-Type", "application/octet-stream");res.status = 200;}
}

客户端模块

功能划分

  • 能够自动检测客户机指定文件夹中的文件,并判断是否需要备份(目前遇到目录是跳过,后期可更新)
    • 将需要备份的文件逐个上传到服务器
  • 备份信息持久化存储与状态恢复

功能细分模块

数据管理

为了更方便的判断指定文件夹中的文件是否是需要备份的文件,将备份的文件信息利用 先描述,再组织 的方式管理起来,并提供将备份的数据信息持久化存储和还原的接口,使得本次进程生命周期结束后不会丢失掉已经管理的备份数据信息,并在下一次进程重启时,加载已经备份的数据信息

由于客户端只需要知道某个文件的大小和其上次修改时间就能够管理这份文件,因此不需要使用json序列化存储,只需要用unordered_map建立简单的映射关系就可以完成管理工作

在本项目中,规定某个文件的唯一标识格式为:文件名-文件大小-文件上次修改时间,建立文件和文件唯一标识的映射关系,快速判断目录内文件是否为需要备份的文件,并在退出客户端时将这个表持久化存储,下一次启动客户端时加载已经备份的信息即可。

class DataManager
{private:std::string _backupFile; //persistent storage file name -- back.datstd::unordered_map<std::string, std::string> _dataTable;public:DataManager(const std::string& backupFile) :_backupFile(backupFile){InitLoad();}//load the info like "a.txt a.txt-123456-20240823" into tablebool InitLoad(){}//persistent storage, for next time to recovervoid Storage(){}//if file is a new one, load into tablevoid Update(const std::string& pathName, const std::string& id){}
};//end DataManager
目录检查
  • c++17–filesystem库

客户端其实本质上就是一个不断对一个文件夹进行检查的死循环,因此需要将这个模块管理起来,提供成员方法来使其模块化,进入循环后,不断扫描检测文件夹,判断其是否满足更新条件,满足则上传到云备份并记录到数据管理模块中

目录模块的实现在公共模块中

文件备份
  • Httplib库

此模块是客户端的主要任务模块,通过不断轮询检查扫描文件夹,判断文件是否需要上传,并完成其中的上传和数据管理的处理,其中上传云备份是最重要的部分,我们使用到了httblib库来完成网络传输

class BackUp
{#define SERVER_IP "xxx"#define SERVER_PORT xxxprivate:std::string _backDir;DataManager* _dataManager;public:explicit BackUp(const std::string& backDir, const std::string& backFile):_backDir(backDir) {}~BackUp(){}std::string CreateIdByPathName(const std::string& pathName){}bool IsNeedUpload(const std::string &pathName){// upload : 1. a new file -- by checking dataTable//			2. an old file which have been modified compared with last time -- by checking identifier// judge if a file are modifing, if a file is not be modified in three second,// it can be considered that this file can be uploaded// a new file or an old file that able to upload}bool Upload(const std::string& pathName){//httplib construct and send by POST}bool RunModule(){while (1){// scan file in dir, add all files into arrayDirUtil dirUtil(_backDir);std::vector<std::string> pathNames;if (!dirUtil.ScanDirectory(&pathNames)){LOG("scan directory error");Sleep(1);}for (auto& pathName : pathNames){// judge if the file should be uploadedif (!IsNeedUpload(pathName)){continue;}// upload if neededif (Upload(pathName)){LOG("upload successfully");_dataManager->Update(pathName, CreateIdByPathName(pathName));}else LOG("upload error");}Sleep(1);LOG("-------------loop once end--------------");}}// end RunModule
};//end BackUp
   //httplib construct and send by POST
}bool RunModule()
{while (1){// scan file in dir, add all files into arrayDirUtil dirUtil(_backDir);std::vector<std::string> pathNames;if (!dirUtil.ScanDirectory(&pathNames)){LOG("scan directory error");Sleep(1);}for (auto& pathName : pathNames){// judge if the file should be uploadedif (!IsNeedUpload(pathName)){continue;}// upload if neededif (Upload(pathName)){LOG("upload successfully");_dataManager->Update(pathName, CreateIdByPathName(pathName));}else LOG("upload error");}Sleep(1);LOG("-------------loop once end--------------");}
}// end RunModule

};//end BackUp

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/52374.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Visual Studio 报错】vs 在使用二进制写入文件时弹窗报错:使用简体中文 gb2312 编码加载文件

如以下报错 解决办法 解决方法&#xff1a;文件->高级保存选项->将文件编码形式改为“UTF-8带签名” 若找不到高级保存选项&#xff0c;可以跟着下面路径把该选项调出来 &#xff1a;工具->自定义->命令->菜单栏中改成文件->预览右边点添加命令->类别中…

【C++ Primer Plus习题】14.1

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream> #include "wine.h" …

传知代码-机器情绪及抑郁症算法(四)!(论文复现)

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 计算机来理解你的情绪&#xff1a;情感计算的发展 近年来&#xff0c;多模态情感分析&#xff08;MSA&#xff09;受到越来越多的关注&#xff0c;多模态情感分析是一个综合了视觉、听觉等语言和非语言信息的重要…

Parsec问题解决方案

Parsec目前就是被墙了&#xff0c;有解决方案但治标不治本&#xff0c;如果想稳定串流建议是更换稳定的串流软件&#xff0c;以下是一些解决方案 方案一&#xff1a;在%appdata%/Parsec/config.txt中&#xff0c;添加代理 app_proxy_address 127.0.0.1 app_proxy_scheme http…

Qt篇——Qt在msvc编译下提示“C2001:常量中有换行符“的错误

在pro文件中添加以下配置即可&#xff1a; msvc{QMAKE_CFLAGS /utf-8QMAKE_CXXFLAGS /utf-8 }

双指针(7)_单调性_三数之和

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 双指针(7)_单调性_三数之和 收录于专栏【经典算法练习】 本专栏旨在分享学习C的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 题目…

Redis 常用命令总结

文章目录 目录 文章目录 1 . 前置内容 1.1 基本全局命令 KEYS EXISTS ​编辑 DEL EXPIRE TTL TYPE 1.2 数据结构和内部编码 2. String类型 SET GET MGET MSET SETNX INCR INCRBY DECR DECYBY INCRBYFLOAT 命令小结 内部编码 3 . Hash 哈希类型 HSET …

gpt4最新保姆级教程

如何使用 WildCard 服务注册 Claude3 随着 Claude3 的震撼发布&#xff0c;最强 AI 模型的桂冠已不再由 GPT-4 独揽。Claude3 推出了三个备受瞩目的模型&#xff1a;Claude 3 Haiku、Claude 3 Sonnet 以及 Claude 3 Opus&#xff0c;每个模型都展现了卓越的性能与特色。其中&a…

数据结构基本知识

一、什么是数据结构 1.1、组织存储数据 ---------》内存&#xff08;存储&#xff09; 1.2、研究目的 如何存储数据&#xff08;变量&#xff0c;数组....)程序数据结构算法 1.3、常见保存数据的方法 数组&#xff1a;保存自己的数据指针&#xff1a;是间接访问已经存在的…

【Vue】pnpm创建Vue3+Vite项目

初始化项目 &#xff08;1&#xff09;cmd切换到指定工作目录&#xff0c;运行pnpm create vue命令&#xff0c;输入项目名称后按需安装组件 &#xff08;2&#xff09;使用vs code打开所创建的项目目录&#xff0c;Ctrl~快捷键打开终端&#xff0c;输入pnpm install下载项目…

[概率论] 随机变量的分布函数 (一)

文章目录 1.随机变量的分布函数2.离散型随机变量的分布函数3.连续性随机变量的分布函数 1.随机变量的分布函数 设X XX是一个随机变量&#xff0c;x xx是任意实数&#xff0c;则函数 几何表示 性质&#xff08;一个函数是分布函数的充要条件&#xff09; 2.离散型随机变量的分布…

数据结构-图-存储-邻接矩阵-邻接表

数据结构-图-存储 邻接矩阵 存储如下图1,图2 图1 对应邻接矩阵 图2 #include<bits/stdc.h> #define MAXN 1005 using namespace std; int n; int v[MAXN][MAXN]; int main(){cin>>n;for(int i1;i<n;i){for(int j1;j<n;j){cin>>v[i][j];}}for(int…

深度解析Unix系统的基本概念及优缺点和原理

介绍 Unix系统是一种多用户、多任务、分时操作系统&#xff0c;起源于20世纪70年代初&#xff0c;由贝尔实验室开发。它具有强大的命令行接口和层次结构的文件系统&#xff0c;支持多种处理器架构&#xff0c;广泛应用于工程应用和科学计算等领域。 基本概念 一、Unix系统的起…

数学建模强化宝典(13)M-K检验法

前言 M-K检验法&#xff0c;全称为Mann-Kendall检验法&#xff0c;是一种非参数的假设检验方法&#xff0c;广泛应用于时间序列数据的趋势性变化检验&#xff0c;特别是气候序列中的趋势分析和突变点检测。以下是对M-K检验法的详细介绍&#xff1a; 一、定义与背景 M-K检验法由…

嵌入式初学-C语言-数据结构--四

栈 1. 基本概念 栈是一种逻辑结构&#xff0c;是特殊的线性表。特殊在&#xff1a; 只能在固定的一端操作 只要满足上述条件&#xff0c;那么这种特殊的线性表就会呈现一种“后进先出”的逻辑&#xff0c;这种逻辑就被称为栈。栈 在生活中到处可见&#xff0c;比如堆叠的盘子…

Kubernetes部署(haproxy+keepalived)高可用环境和办公网络打通

HAProxy Keepalived 部署高可用性入口&#xff1a; 部署两台或多台节点运行 HAProxy 作为负载均衡器。使用 Keepalived 实现 VIP&#xff08;虚拟 IP&#xff09;&#xff0c;为 HAProxy 提供高可用性。Keepalived 会监控 HAProxy 的状态&#xff0c;如果主节点失效&#xff0…

Spring AOP,通知使用,spring事务管理,spring_web搭建

spring AOP AOP概述 AOP面向切面编程是对面向对象编程的延续&#xff08;AOP &#xff08;Aspect Orient Programming&#xff09;,直译过来就是 面向切面编程,AOP 是一种编程思想&#xff0c;是面向对象编程&#xff08;OOP&#xff09;的一种补充。&#xff09; 面向切面编…

【C++二分查找】1482. 制作 m 束花所需的最少天数

本文涉及的基础知识点 C二分查找 LeetCode1482. 制作 m 束花所需的最少天数 给你一个整数数组 bloomDay&#xff0c;以及两个整数 m 和 k 。 现需要制作 m 束花。制作花束时&#xff0c;需要使用花园中 相邻的 k 朵花 。 花园中有 n 朵花&#xff0c;第 i 朵花会在 bloomDay…

南京网站设计手机用的网站

近年来&#xff0c;随着移动互联网的快速发展&#xff0c;越来越多的用户通过手机浏览网页&#xff0c;这使得网站设计逐渐向移动端倾斜。在南京&#xff0c;网站设计特别注重适配手机端&#xff0c;这不仅是用户体验的提升&#xff0c;也是市场竞争的需要。一个响应式的网站能…

vue3 v-bind=“$attrs“ 的一些理解,透传 Attributes相关说明及事例说明

1、可能小伙伴们经常会在自己的项目中看到v-bind"$attrs"&#xff0c;这个一般是在自定义组件中看到。 比如&#xff1a; <template><BasicModalv-bind"$attrs"register"registerModal":title"getTitle"ok"handleSubm…