网站建设费用预算表、学大教育培训机构怎么样
文章目录
- 专栏导读
- 1.客户端数据管理模块实现
- 2.客户端文件检测模块实现
- 3.客户端文件备份模块设计
- 4.客户端文件备份模块实现
专栏导读
🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。
🌸专栏简介:本文收录于 C++项目——云备份
🌸相关专栏推荐:C语言初阶系列、C语言进阶系列 、C++系列、数据结构与算法、Linux
🌸项目Gitee链接:https://gitee.com/li-yuanjiu/cloud-backup
1.客户端数据管理模块实现
客户端要实现的功能是对指定文件夹中的文件自动进行备份上传
。但是并不是所有的文件每次都需要上传,我们需要能够判断,哪些文件需要上传,哪些不需要,因此需要将备份的文件信息给管理起来,作为下一次文件是否需要备份的判断。因此需要被管理的信息包含以下:
文件路径名称
;文件唯一标识
:由文件名,最后一次修改时间,文件大小组成的一串信息;
客户端数据管理模块可直接由服务端数据管理模块改造得到,因为其只需要服务端数据管理模块代码中一小部分功能。
#ifndef __MY_DATA__
#define __MY_DATA__
#include <unordered_map>
#include <sstream>
#include "util.hpp"namespace cloud
{class DataManager{public:DataManager(const std::string &backup_file) :_backup_file(backup_file){InitLoad();}bool Storage(){// 1.获取所有的备份信息std::stringstream ss;auto it = _table.begin();for (; it != _table.end(); ++it){// 2.将所有信息进行指定持久化格式的组织ss << it->first << " " << it->second << "\n";}// 3.持久化存储FileUtil fu(_backup_file);fu.SetContent(ss.str());return true;}bool InitLoad(){// 1.从文件中读所有数据FileUtil fu(_backup_file);std::string body;fu.GetContent(&body);// 2.进行数据解析,添加到表当中std::vector<std::string> array;Split(body, "\n", &array);for (auto& a : array){std::vector<std::string> tmp;Split(a, " ", &tmp);if (tmp.size() != 2){continue;}_table[tmp[0]] = tmp[1];}return true;}bool Insert(const std::string &key, const std::string &val){_table[key] = val;Storage();return true;}bool Updata(const std::string& key, const std::string& val){_table[key] = val;Storage();return true;}bool GetOneByKey(const std::string& key, std::string* val){auto it = _table.find(key);if (it == _table.end()){return false;}*val = it->second;return true;}private:int Split(const std::string &str, const std::string &sep, std::vector<std::string>* array){int count = 0;size_t pos = 0, idx = 0;while (1){pos = str.find(sep, idx);if (pos == std::string::npos){break;}if (pos == idx){idx = pos + sep.size();continue;}std::string tmp = str.substr(idx, pos - idx);array->push_back(tmp);count++;idx = pos + sep.size();}if (idx < str.size()){array->push_back(str.substr(idx));count++;}return count;}private:std::string _backup_file; // 备份信息持久化存储文件std::unordered_map<std::string, std::string> _table;};
}
#endif
2.客户端文件检测模块实现
同样的,客户端文件实用工具类其实与服务端的文件实用工具类雷同,只是功能需求并没有服务端那么多,复制过来即可。
#ifndef __MY_UTIL__
#define __MY_UTIL__
/*1.获取文件大小2.获取文件最后一次修改时间3.获取文件最后一次访问时间4.获取文件路径名中的文件名称 /abc/test.txt -> test.txt5.向文件写入数据6.获取文件数据7.获取文件指定位置 指定数据长度8.判断文件是否存在9.创建目录10.浏览获取目录下的所有文件路径名11.压缩文件12.解压缩所有文件
*/
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <experimental/filesystem>
#include <sys/stat.h>namespace cloud
{namespace fs = std::experimental::filesystem;class FileUtil{public:FileUtil(const std::string &filename) :_filename(filename){}int64_t FileSize(){struct stat st;if(stat(_filename.c_str(), &st) < 0){std::cout << "get file size failed!" << std::endl;return 0;}return st.st_size;}time_t LastMTime(){struct stat st;if(stat(_filename.c_str(), &st) < 0){std::cout << "get last modify time failed!" << std::endl;return -1;}return st.st_mtime;}time_t LastATime(){struct stat st;if(stat(_filename.c_str(), &st) < 0){std::cout << "get last access time failed!" << std::endl;return -1;}return st.st_atime; }std::string FileName(){size_t pos = _filename.find_last_of("/");if(pos == std::string::npos){return _filename;}return _filename.substr(pos+1);}bool GetPosLen(std::string *body, size_t pos, size_t len){size_t fsize = FileSize();if(pos + len > fsize){std::cout << "get file len error" << std::endl;return false;}std::ifstream ifs;ifs.open(_filename, std::ios::binary);if(ifs.is_open() == false){std::cout << "open file failed!" << std::endl;return false;}ifs.seekg(pos, std::ios::beg);body->resize(len);ifs.read(&(*body)[0], len);if(ifs.good() == false){std::cout << "get file content failed" << std::endl;ifs.close();return false;}ifs.close();return true;}bool GetContent(std::string *body){size_t fsize = FileSize();return GetPosLen(body, 0, fsize);}bool SetContent(const std::string &body){std::ofstream ofs;ofs.open(_filename, std::ios::binary);if(ofs.is_open() == false){std::cout << "write open file failed" << std::endl;return false;}ofs.write(&body[0], body.size());if(ofs.good() == false){std::cout << "write open file failed" << std::endl;ofs.close();return false;}ofs.close();return true;}bool Exists(){return fs::exists(_filename);}bool Remove(){if(Exists() == false){return true;}remove(_filename.c_str());return true;}bool CreateDirectory(){if(Exists()) return true;return fs::create_directories(_filename);}bool ScanDirectory(std::vector<std::string> *array){CreateDirectory();for(auto& p : fs::directory_iterator(_filename)){if(fs::is_directory(p) == true) continue;// relative_path 带有路径的文件名array->push_back(fs::path(p).relative_path().string());}return true;}private:std::string _filename;};
}
#endif
3.客户端文件备份模块设计
回顾客户端自动将指定文件夹中的文件备份到服务器流程:
遍历指定文件夹
;逐一判断文件是否需要备份
;需要备份的文件进行上传备份
;
客户端文件备份类主要包含以下成员:
#define SERVER_ADDR "47.108.25.253" // 服务器IP
#define SERVER_PORT 8989 // 服务器端口
class Backup
{
public:Backup(const std::string& back_dir, const std::string& back_file);// 生成文件的唯一标识std::string GetFileIdentifier(std::string filename);// 上传文件函数bool Upload(const std::string& filename); // 判断是否需要上传bool IsNeedUpload(const std::string& filename);// 主逻辑执行函数bool RunMoudle();
private:std::string _back_dir; // 监控的文件目录DataManager* _data;
};
4.客户端文件备份模块实现
#ifndef __MY_CLOUD__
#define __MY_CLOUD__
#include "data.hpp"
#include "httplib.h"
#include <Windows.h>
#define SERVER_ADDR "47.108.25.253"
#define SERVER_PORT 8989
namespace cloud
{class Backup{public:Backup(const std::string& back_dir, const std::string& back_file):_back_dir(back_dir){_data = new DataManager(back_file);}std::string GetFileIdentifier(std::string filename){FileUtil fu(filename);std::stringstream ss;ss << fu.FileName() << "-" << fu.FileSize() << "-" << fu.LastMTime();return ss.str();}bool Upload(const std::string& filename){// 1.获取文件数据FileUtil fu(filename);std::string body;fu.GetContent(&body);// 2.搭建http客户端上传文件数据httplib::Client client(SERVER_ADDR, SERVER_PORT);httplib::MultipartFormData item;item.content = body;item.filename = fu.FileName();item.content_type = "application/octet-stream";httplib::MultipartFormDataItems items;items.push_back(item);auto res = client.Post("/upload", items);if (!res || res->status != 200){return false;}return true;}bool IsNeedUpload(const std::string& filename){// 需要上传的文件判断条件:文件是新增的,不是新增的但是被修改过// 文件是新增的:看一下有没有备份信息// 不是新增的但是被修改过:有历史信息,但是历史信息的唯一标识符与当前最新的唯一标识符不一致std::string id;if (_data->GetOneByKey(filename, &id) != false){// 有历史信息std::string new_id = GetFileIdentifier(filename);if (new_id != id){return false;}}//判断一个文件是否有一段时间没有被修改过了FileUtil fu(filename);if (time(NULL) - fu.LastMTime() < 3){return false;}return true;}bool RunMoudle(){while (1){// 1.遍历获取指定文件夹中所有文件FileUtil fu(_back_dir);std::vector<std::string> array;fu.ScanDirectory(&array);// 2.逐个判断是否需要上传for (auto& a : array){if (IsNeedUpload(a) == false)continue;// 3.如果需要,则上传文件if (Upload(a) == true){_data->Insert(a, GetFileIdentifier(a));}}Sleep(1);}}private:std::string _back_dir;DataManager* _data;};
}
#endif