当前位置: 首页 > news >正文

【项目】云备份

云备份

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

云备份

概述

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

框架

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

本项目有三大模块:

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

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

  • 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

相关文章:

【项目】云备份

云备份 云备份概述框架 功能演示服务端客户端 公共模块文件操作模块目录操作模块 服务端模块功能划分功能细分模块数据管理热点管理 客户端模块功能划分功能细分模块数据管理目录检查文件备份 云备份 概述 自动将本地计算机上指定文件夹中需要备份的文件上传备份到服务器中。…...

WebGL系列教程二(环境搭建及初始化Shader)

目录 1 前言2 新建html页面3 着色器介绍3.1 顶点着色器、片元着色器与光栅化的概念3.2 声明顶点着色器3.3 声明片元着色器 4 坐标系(右手系)介绍5 着色器初始化5.1 给一个画布canvas5.2 获取WebGL对象5.3 创建着色器对象5.4 获取着色器对象的源5.5 绑定着色器的源5.6 编译着色器…...

keepalive和nginx高可用集群

keepalived 和 nginx 高可用集群搭建 主备模式 zyj86主机和zyj87主机安装nginx和keepalived yum install nginx keepalived -y systemctl enable --now nginx.service keepalived.service主调度器配置 编辑zyj86主机&#xff08;主&#xff09;配置文件 vi /etc/keepalived…...

二分查找题总结

二分查找题总结 hot100搜索插入位置搜索二维矩阵在排序数组中查找元素的第一个和最后一个位置搜索旋转排序数组寻找旋转排序数组中的最小值寻找两个正序数组的中位数 hot100 搜索插入位置 题目链接&#xff1a; 35.搜索插入位置 代码&#xff1a; class Solution {public in…...

仕考网:公务员面试流程介绍

通知进面信息——资格审查——面试签到——抽签候考 面试形式&#xff1a; 面试分为结构化和无领导小组两种形式 1.在结构化面试中&#xff0c;当轮到某位考生时&#xff0c;引导员将在候考室宣布其编号&#xff0c;随后考生跟随引导人员前往考场入口。考生在开始考试时需回…...

(十五)SpringCloudAlibaba-Sentinel持久化到Nacos

前言 在前面我们已经将Sentinel配置的规则持久化到系统的文件中。本章节我们将Sentinel持久化到Nacos中; 传送门(Sentinel数据持久化到文件)https://blog.csdn.net/weixin_45876411/article/details/140742963 默认情况下 Sentinel 只能接收到 Nacos 推送的消息&#xff0c;但…...

GitHub图床

GitHub图床 文章目录 GitHub图床图床介绍Github访问GitHub手动修改hostsgithub520 加速器创建账户创建仓库创建token PicGoTypora 图床介绍 图床 存放图片的地方 为什么设置图床呢 在我认识图床之前, 有一个问题 [^放在typora上面的图片, 其实是一个链接, 并且将图片存放在本地…...

记一次高版本view-design的组件迁移到自身项目的低版本

背景 npm i -S view-design当前老项目使用view-design这个组件库&#xff0c;但是当我们去官网查看该组件库最新版本&#xff0c;竟然发现没有博主想用的image/ImagePreivew这两个基础组件 说实话&#xff0c;有点离谱了哈&#xff01;&#xff01; 自己造轮子&#xff1f; …...

QT运行ROS工程

文章目录 使用QT创建ROS工程项目配置修改cmake环境配置运行设置 运行 使用QT创建ROS工程 工程名字和路径 下一步(直接选择默认选项就可以&#xff09;->完成 完成之后 是这样的 接下来在工作空间里面创建功能包 鼠标选中src点击右键->添加新文件 name::功能包的名字…...

电脑技巧:如何在Win11电脑上调整设置,让屏幕更加护眼?

目录 一、调整屏幕亮度 二、启用夜间模式 三、调整色彩设置 四、使用第三方护眼软件 五、保持良好的用眼习惯 总结 随着长时间使用电脑的人越来越多,护眼问题也变得越来越重要。Win11作为更新的操作系统,提供了更多的设置选项来帮助我们保护眼睛。本文将详细介绍如何在…...

【数据结构】排序算法篇二

【数据结构】排序算法篇二 1. 快速排序&#xff08;hoare版本&#xff09;&#xff08;1&#xff09;基本思想&#xff1a;&#xff08;2&#xff09;动态图解&#xff1a;&#xff08;3&#xff09;代码实现&#xff1a;&#xff08;4&#xff09;特性总结&#xff1a; 2. 快速…...

python进阶篇-day09-数据结构与算法(非线性结构与排序算法)

非线性结构(树状结构) 特点: 每个节点都可以有n个子节点(后继节点) 和 n个父节点(前驱节点) 代表: 树, 图...... 概述 属于数据结构之 非线性结构的一种, 父节点可以有多个子节点(后续节点) 特点 有且只有1个根节点 每个节点都可以有1个父节点及任意个子节点, 前提: 根节点除…...

线性代数基础

Base 对于矩阵 A&#xff0c;对齐做 SVD 分解&#xff0c;即 U Σ V s v d ( A ) U\Sigma V svd(A) UΣVsvd(A). 其中 U 为 A A T AA^T AAT的特征向量&#xff0c;V 为 A T A A^TA ATA的特征向量。 Σ \Sigma Σ 的对角元素为降序排序的特征值。显然&#xff0c;U、V矩阵…...

LCR 021

题目&#xff1a;LCR 021 解法一&#xff1a;计算链表长度 遍历两次&#xff0c;第一次获取链表长度 L&#xff08;包括虚拟节点&#xff09;&#xff0c;第二次遍历到第 L-n 个节点&#xff08;从虚拟节点遍历&#xff09; public ListNode removeNthFromEnd(ListNode head, …...

【阿雄不会写代码】全国职业院校技能大赛GZ036第四套

也不说那么多了&#xff0c;要用到这篇博客&#xff0c;肯定也知道他是干嘛的&#xff0c;给博主点点关注点点赞&#xff01;&#xff01;&#xff01;这样博主才能更新更多免费的教程&#xff0c;不然就直接丢付费专栏里了&#xff0c;需要相关文件请私聊...

Vue组件:使用$emit()方法监听子组件事件

1、监听自定义事件 父组件通过使用 Prop 为子组件传递数据&#xff0c;但如果子组件要把数据传递回去&#xff0c;就需要使用自定义事件来实现。父组件可以通过 v-on 指令&#xff08;简写形式“”&#xff09;监听子组件实例的自定义事件&#xff0c;而子组件可以通过调用内建…...

数据分析-埋点

1、数据埋点的定义 针对特定用户行为或事件进行捕获、处理何发送的相关技术及其实施过程。 2、数据埋点的原理 埋点是数据采集的重要方式。通过在页面上植入代码&#xff0c;监控用户行为(例:页面加载、按钮点击等)。用户一旦触发了该事件&#xff0c;就会根据埋点信息将相关数…...

【文心智能体】通过工作流使用知识库来实现信息查询输出,一键查看旅游相关信息,让出行多一份信心

欢迎来到《小5讲堂》 这是《文心智能体平台》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 创建灵感基本配置头像名称和简介人物设定角色与目标思考路…...

服务器监控工具都是监控服务器的哪些性能和指标

服务器监控工具通常用于确保服务器及其相关服务的正常运行。这些工具可以帮助管理员快速识别并解决问题&#xff0c;从而减少停机时间和性能下降的风险。以下是服务器监控工具通常会监控的一些主要内容&#xff1a; 系统健康状态&#xff1a; CPU使用率 内存&#xff08;RAM&…...

不小心删除丢失了所有短信?如何在 iPhone 上查找和恢复误删除的短信

不小心删除了一条短信&#xff0c;或者丢失了所有短信&#xff1f;希望还未破灭&#xff0c;下面介绍如何在 iPhone 上查找和恢复已删除的短信。 短信通常都是非正式和无关紧要的&#xff0c;但短信中可能包含非常重要的信息。因此&#xff0c;如果您删除了一些短信以清理 iPh…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)

第一篇&#xff1a;Liunx环境下搭建PaddlePaddle 3.0基础环境&#xff08;Liunx Centos8.5安装Python3.10pip3.10&#xff09; 一&#xff1a;前言二&#xff1a;安装编译依赖二&#xff1a;安装Python3.10三&#xff1a;安装PIP3.10四&#xff1a;安装Paddlepaddle基础框架4.1…...