云备份day05

📟作者主页:慢热的陕西人

🌴专栏链接:C++云备份项目

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

主要内容实现Json实用类的设计,以及服务端的设计和实现,客户端的设计和实现

在这里插入图片描述

文章目录

  • 云备份day05
    • 1.Json实用工具类设计与实现
    • 2.配置文件加载模块
      • 2.1基本需求:
      • 2.2系统运行配置信息的单例类的设计
    • 3.数据管理类的设计
      • 3.1基本需求
      • 3.2数据管理类的设计
    • 4.热点管理模块
    • 5.服务端业务处理模块
      • 5.1服务端业务处理模块的设计
      • 5.2服务端业务处理模块的实现
    • 6.客户端业务处理模块
      • 6.1客户端功能和模块化设计
      • 6.2移植一部分代码到windows下的vs
      • 6.3数据管理类的设计
      • 6.4文件备份类的设计

云备份day05

1.Json实用工具类设计与实现

上面的代码定义了一个名为JsonUtil的类,其中包含了两个静态方法:Serialize和UnSerialize,用于序列化和反序列化JSON对象。

  • Serialize 方法接受一个 Json::Value 类型的参数 root,表示要序列化的JSON对象,以及一个指向 std::string 对象的指针 str,用于存储序列化后的JSON字符串。在方法内部,它使用JSONcpp提供的 Json::StreamWriter 将JSON对象序列化到一个 std::stringstream 对象中,并将其转换为字符串存储到指定的 std::string 对象中。

  • UnSerialize 方法接受一个 std::string 类型的参数 str,表示要反序列化的JSON字符串,以及一个指向 Json::Value 对象的指针 root,用于存储反序列化后的JSON对象。在方法内部,它使用JSONcpp提供的 Json::CharReader 尝试解析传入的JSON字符串,并将解析后的结果存储到指定的 Json::Value 对象中。

这些方法都是静态的,因此可以直接通过类名调用,无需创建类的实例。这段代码使用了JSONcpp库来处理JSON数据的序列化和反序列化,但在编译时出现了链接错误,可能是由于未正确链接JSONcpp库或未正确包含JSONcpp的头文件导致的。

    class JsonUtil{public:static bool Serialize(const Json::Value &root, std::string *str){// 序列化JSON对象。Json::StreamWriterBuilder swb; // 创建StreamWriterBuilder对象,用于构建StreamWriter。std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter()); // 使用StreamWriterBuilder创建StreamWriter的unique_ptr。std::stringstream ss;                                          // 创建一个stringstream对象,用作序列化的输出缓冲区。if(0 != sw->write(root, &ss))// 使用StreamWriter将JSON对象序列化到stringstream。{std::cout << "Json Write failed!\n";return false;}                                    *str = ss.str();return true;}static bool UnSerialize(std::string &str, Json::Value *root){Json::CharReaderBuilder crb;                          // 创建CharReaderBuilder对象。std::unique_ptr<Json::CharReader> cr(crb.newCharReader()); // 使用CharReaderBuilder创建CharReader的unique_ptr。std::string err;                                           // 定义字符串以存储可能的错误信息。// 尝试解析JSON字符串。bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);if (ret == false) // 检查解析是否成功,这里有个逻辑错误,应该使用==比较。{std::cout << err << std::endl; // 如果解析失败,输出错误信息。return false;      }return true;}};

2.配置文件加载模块

2.1基本需求:

配置信息
1.热点判断时间热点管理:多长时间没有被访问的文件算是非热点文件

2.文件下载的url前缀路径–用于表示客户端请求是一个下载请求url: http://192.168.122.136:9090/path当用户发来一个备份列表查看请求 /listshow,我们如何判断这个不是一个listshow的文件下载请求
/download/test.txt,/download/listshow

3.压缩包后缀名:订立的压缩包命名规则,就是在文件原名称之后加上后缀。".z’4.上传文件存放路径:决定了文件上传之后实际存储在服务器的哪里

5.压缩包存放路径:决定非热点文件压缩后存放的路径
6.服务端备份信息存放文件:服务端记录的备份文件信息的持久化存储7.服务器的监听IP地址:当程序要运行在其他主机上,则不需要修改程序
8.服务器的监听端口

2.2系统运行配置信息的单例类的设计

// 系统运行配置信息的单例类设计
class Config
{
public:// 获取Config类的唯一实例static Config *GetInstance();// 获取热点判断时间int GetHotTime();// 获取服务器监听端口int GetServerPort();// 获取下载的URL前缀路径std::string GetDownloadPrefix();// 获取压缩包后缀名称std::string GetPackFileSuffix();// 获取备份文件存放目录std::string GetBackDir();// 获取压缩包存放目录std::string GetPackDir();// 获取服务器IP地址std::string GetServerIp();// 获取数据信息存放文件std::string GetBackupFile();private:// 私有构造函数,防止外部实例化Config() {}private:static std::mutex _mutex; // 互斥锁,用于保护_instance的创建过程static Config *_instance; // 唯一实例指针int _hotTime;                 // 热点判断时间int _serverPort;              // 服务器监听端口std::string _downloadPrefix;  // 下载的URL前缀路径std::string _packFileSuffix;  // 压缩包后缀名称std::string _backDir;         // 备份文件存放目录std::string _packDir;         // 压缩包存放目录std::string _serverIp;        // 服务器IP地址std::string _backupFile;      // 数据信息存放文件
};

填充:

这个类主要实现了以下功能:

  1. 读取配置文件: 类内部有一个私有方法 ReadConfigFile(),用于读取配置文件的内容,并将配置项的值保存在类的成员变量中。

  2. 单例模式: 类内部使用了单例模式,通过私有的构造函数和静态的 GetInstance() 方法确保只有一个 Config 实例被创建。在 GetInstance() 方法中,通过加锁的方式保证了在多线程环境下的线程安全性。

  3. 配置项访问: 类提供了一系列的公有方法,用于获取各种配置项的值,例如获取热点时间、服务器端口、服务器 IP 等。

  4. 互斥锁保护: 类内部使用了 std::mutex 作为静态成员变量 _mutex,用于保护单例模式的实现,在多线程环境下确保线程安全。

  5. 错误处理: 在读取配置文件过程中,如果出现错误,会输出相应的错误信息,并返回 false,表示读取配置文件失败。

总的来说,这个类的主要功能是读取配置文件并提供配置项的访问方法,同时使用了单例模式和互斥锁保证了线程安全性。

#ifndef MY_CONFIG__
#define MY_CONFIG__#include <mutex>
#include "Util.hpp"namespace cloud
{// 定义配置文件路径#define CONFIG_FILE "./cloud.conf"// 配置类,用于读取配置文件class Config{private:// 构造函数私有化,确保单例模式Config(){ReadConfigFile();}// 读取配置文件的私有方法bool ReadConfigFile(){// 使用文件工具类读取配置文件内容FileUtil fu(CONFIG_FILE);std::string body;// 如果读取配置文件失败,则返回 falseif(fu.GetContent(&body) == false){std::cout << "load config file failed!\n";return false;}// 使用 JSON 解析工具类解析配置文件内容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();_back_dir = root["back_dir"].asString();_pack_dir = root["pack_dir"].asString();_backup_file = root["backup_file"].asString();return true;}public:// 获取配置单例的静态方法static Config* GetInstance(){// 如果实例不存在,则创建实例if(NULL == _instance){_mutex.lock();if (NULL == _instance){_instance = new Config();}_mutex.unlock();}return _instance;}// 获取热点时间配置项int GetHotTime(){return _hot_time;}// 获取服务器端口配置项int GetServerPort(){return _server_port;}// 获取服务器 IP 配置项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;}private:// 配置项成员变量int _hot_time;int _server_port;std::string _server_ip;std::string _download_prefix;std::string _packfile_suffix;std::string _back_dir;std::string _pack_dir;std::string _backup_file;// 单例实例和互斥锁静态成员变量static Config* _instance;static std::mutex _mutex;    };// 静态成员变量初始化Config* Config::_instance = NULL;std::mutex Config::_mutex;
}#endif

3.数据管理类的设计

3.1基本需求

**数据管理模块:**需要管理的数据有哪些
管理哪些数据,是因为后期要用到哪些数据
1.文件的实际存储路径:当客户端要下载文件时,则从这个文件中读取数据进行响应

2.文件压缩包存放路径名:如果这个文件是一个非热点文件会被压缩,则这个就是压缩包路径名称如果客户端要下载文件,则需要先解压缩,然后读取解压后的文件数据。

3.文件是否压缩的标志位:判断文件是否已经被压缩了
4.文件大小
5.文件最后一次修改时间
6.文件最后一次访问时间
7.文件访问URL中资源路径path:/download/a.txt

如何管理数据:
1.用于数据信息访问:使用hash表在内存中管理数据,以url的path作为key值–查询速度快

2.持久化存储管理:使用ison序列化将所有数据信息保存在文件中

3.2数据管理类的设计

// 数据信息结构体: 用于存储备份信息
typedef struct BackupInfo_t
{bool pack_flag;          // 是否压缩标志size_t fsize;            // 文件大小time_t atime;            // 最后一次访问时间time_t mtime;            // 最后一次修改时间std::string real_path;   // 文件实际存储路径名称std::string pack_path;   // 压缩包存储路径名称
<<<<<<< HEADstd::string url;    // URL路径
=======std::string url_path;    // URL路径
>>>>>>> 88cf706b46ca916ba52c492de16f7fa03814e361
} BackupInfo;// 数据管理类: 管理服务端系统中会用到的数据
class DataManager
{
private:std::string backup_file;                                 // 持久化存储文件std::unordered_map<std::string, BackupInfo> table;       // 内存中以哈希表存储pthread_rwlock_t rwlock;                                 // 读写锁--读共享,写互斥。
public:    // 构造函数DataManager();// 每次数据新增或修改都要重新持久化存储,避免数据丢失bool Storage(); // 初始化加载,在每次系统重启都要加载以前的数据bool InitLoad();<<<<<<< HEAD//将数据持久化到文件bool Storage();=======
>>>>>>> 88cf706b46ca916ba52c492de16f7fa03814e361// 新增备份信息bool Insert(const BackupInfo &info);// 修改备份信息bool Update(const BackupInfo &info);// 根据URL获取备份信息bool GetOneByUrl(const std::string &url, BackupInfo *info);// 根据实际路径获取备份信息bool GetOneByRealpath(const std::string &path, BackupInfo *info);// 获取所有备份信息bool GetAll(std::vector<BackupInfo> *array);
};// 创建新的备份信息对象
void NewBackupInfo(const std::string realpath, BackupInfo *info);

<<<<<<< HEAD
实现:

#ifndef _MY_DATA__
#define _MY_DATA__#include "Util.hpp"
#include <unordered_map>
#include <pthread.h>
#include "config.hpp"namespace cloud
{typedef struct BackupInfo{bool pack_flag;        // 是否压缩标志size_t fsize;          // 文件大小time_t atime;          // 最后一次访问时间time_t mtime;          // 最后一次修改时间std::string real_path; // 文件实际存储路径名称std::string pack_path; // 压缩包存储路径名称std::string url;  // URL路径// 创建新的备份信息对象bool NewBackupInfo(const std::string& realpath){cloud::FileUtil fu(realpath);if(fu.Exists() == false){std::cout << "New BackupInfo File not exists" << std::endl;return false;}            Config* config = Config::GetInstance();std::string packdir = config->GetPackDir();std::string packsuffix = config->GetPackFileSuffix();std::string download_preffix = config->GetDownloadPrefix();this->pack_flag = false;this->fsize = fu.FileSize();this->mtime = fu.LastMTime();this->atime = fu.LastATime();this->real_path = realpath;// ./packdir/a.txt ---> ./packdir/a.txt.lzthis->pack_path = packdir + fu.FileName() + packsuffix;// ./packdir/a.txt ---> /download/a.txtthis->url = download_preffix + fu.FileName();return true;}}BackupInfo;class DataManager{public:DataManager(){_backup_file = Config::GetInstance()->GetBackupFile();pthread_rwlock_init(&_rwlock, nullptr); //初始化读写锁InitLoad();}~DataManager(){pthread_rwlock_destroy(&_rwlock); //销毁读写锁}bool Insert(const BackupInfo &info){pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;pthread_rwlock_unlock(&_rwlock);Storage();return true;}bool Updata(const BackupInfo &info){pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;pthread_rwlock_unlock(&_rwlock);Storage();return true;}bool GetOneByURL(const std::string& url, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock); //因为url是key,所以我们可以选择直接查找的方法auto it = _table.find(url);if(it == _table.end()){pthread_rwlock_unlock(&_rwlock);return false;}   *info = it->second;pthread_rwlock_unlock(&_rwlock);return true;}bool GetOneByRealPath(const std::string& realpath, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock); auto it = _table.begin();for(; it != _table.end(); ++it){if(it->second.real_path == realpath){*info = it->second;pthread_rwlock_unlock(&_rwlock);return true;}}pthread_rwlock_unlock(&_rwlock);return false;}bool GetAll(std::vector<BackupInfo> *arry){pthread_rwlock_wrlock(&_rwlock);auto it = _table.begin();for (; it != _table.end(); ++it)arry->push_back(it->second);pthread_rwlock_unlock(&_rwlock);return true; }bool InitLoad(){//1.将数据文件中的数据读取出来FileUtil fu(_backup_file);if(fu.Exists() == false){std::cout << "InitLoad file not eixst!\n";return false;}std::string body;fu.GetContent(&body);//2.反序列化Json::Value root;JsonUtil::UnSerialize(body, &root);//3.将反序列化得到的内容添加到table中for(int i = 0; i < root.size(); ++i){BackupInfo info;info.pack_flag = root[i]["pack_flag"].asBool();info.fsize = root[i]["fsize"].asInt64();info.atime = root[i]["atime"].asInt64();info.mtime = root[i]["mtime"].asInt64();info.real_path = root[i]["real_path"].asString();info.pack_path = root[i]["pack_path"].asString();info.url = root[i]["url"].asString();Insert(info);}return true;}bool Storage(){//1.获取所有数据std::vector<BackupInfo> arry;this->GetAll(&arry);//2.添加到Json::ValueJson::Value root;for(int i = 0; i < arry.size(); ++i){Json::Value item;item["pack_flag"] = arry[i].pack_flag;item["fsize"] = (Json::Int64)arry[i].fsize;item["atime"] = (Json::Int64)arry[i].atime;item["mtime"] = (Json::Int64)arry[i].mtime;item["real_path"] = arry[i].real_path;item["pack_path"] = arry[i].pack_path;item["url"] = arry[i].url;root.append(item); //添加数组元素}//3.对Json::Value进行序列化std::string body;JsonUtil::Serialize(root, &body);//4.写文件FileUtil fu(_backup_file);fu.SetContent(body);return true;}private:std::string _backup_file;pthread_rwlock_t _rwlock; std::unordered_map<std::string, BackupInfo> _table;};}#endif

4.热点管理模块

热点管理模块:对服务器上备份的文件进行检测,哪些文件长时间没有被访问,则认为是非热点文件,则压缩存储,节省磁盘空间。
实现思路:
遍历所有的文件,检测文件的最后一次访问时间,与当前时间进行相减得到差值,这个差值如果大于设定好的非热点判断时间则认为是非热点文件,则进行压缩存放到压缩路径中,除源文件遍历所有的文件:
1.从数据管理模块中遍历所有的备份文件信息

​ 2.遍历备份文件夹,获取所有的文件进行属性获取,最终判断

​ 选择第二种:遍历文件夹,每次获取文件的最新数据进行判断,并且还可以解决数据信息缺漏的问题

1.遍历备份目录,获取所有文件路径名称
2.逐个文件获取最后一次访问时间与当前系统时间进行比较判断
3.对非热点文件进行压缩处理,删除源文件
4.修改数据管理模块对应的文件信息(压缩标志->true)

extern DataManager *data;
class HotManager
{
private:std::string back dir; // 备份文件路径std::string _pack_dir;//压缩文件路径std::string _pack_suffix;//压缩包后缀名int hot time; //热点判断时间
public:HotManager() bool RunModule();
}

实现:

#ifndef __MY_HOT__
#define __MY_HOT__#include "data.hpp"
#include <unistd.h>extern cloud::DataManager* _data;
namespace cloud
{class HotManager{public:HotManager(){Config* config = Config::GetInstance();_back_dir = config->GetBackDir();_pack_dir = config->GetPackDir();_pack_suffix = config->GetPackFileSuffix();_hot_time = config->GetHotTime();FileUtil tmp1(_back_dir);FileUtil tmp2(_pack_dir);tmp1.CreateDirectory();tmp2.CreateDirectory();}//非热点文件返回真,热点文件返回假bool HotJudge(const std::string &filename){FileUtil fu(filename);time_t last_atime = fu.LastATime();time_t cur_time = time(NULL);if(cur_time - last_atime > _hot_time){return true;}return false;}bool RunModule(){while(1){// 1.遍历备份文件,获取所有文件名FileUtil fu(_back_dir);std::vector<std::string> arry;fu.ScanDirectory(&arry);// 2.遍历判断文件是否是非热点文件for (auto &a : arry){if (HotJudge(a) == false){continue; // 热点文件不需要处理}// 4.获取文件备份信息BackupInfo bi;if (false == _data->GetOneByRealPath(a,&bi)){// 现在有一个文件存在,但是没有备份信息bi.NewBackupInfo(a); // 设置一个新的备份信息}// 4.对非热点文件进行压缩处理FileUtil tmp(a);tmp.Compress(bi.pack_path);// 4.删除源文件tmp.Remeove();bi.pack_flag = true;_data->Updata(bi);}usleep(1000); //避免空目录,频繁循环,消耗cpu资源}return true;}private:std::string _back_dir;std::string _pack_dir;std::string _pack_suffix;int _hot_time;};}#endif

5.服务端业务处理模块

5.1服务端业务处理模块的设计

服务端业务处理模块:将网络通信模块和业务进行了合并(网络通信通过httplib库完成)

1.搭建网络通信服务器: 借助httplib完成

2.业务请求处理

​ 1.文件上传请求:备份客户端上传的文件响应上传成功
​ 2.文件列表请求:客户端浏览器请求一个备份文件的展示页面,响应页面载,响应客户端要下载的文件数据 3.文件下载请求:通过展示页面,点击下载, 响应客户端要下载的文件数据

image-20240421214318587

代码框架设计:

image-20240421214930641

Etag字段:

image-20240422113709461

5.2服务端业务处理模块的实现

上传,下载,展示

#ifndef __MY_SERVER__
#define __MY_SERVER__#include<ctime>
#include<string>
#include "data.hpp"
#include "httplib.h"extern cloud::DataManager* _data;
namespace cloud
{class Service{private:static void Upload(const httplib::Request &req, httplib::Response &rsp){auto ret = req.has_file("file"); // 判断有没有上传文件区域if (ret == false){rsp.status = 400;return;}// 在云端创建对应的文件,并且将上传的文件中的信息拷贝到对应的文件const auto &file = req.get_file_value("file");std::string back_dir = Config::GetInstance()->GetBackDir();std::string realpath = back_dir + FileUtil(file.filename).FileName();FileUtil fu(realpath);fu.SetContent(file.content);// 将备份文件的文件信息插入到表中BackupInfo info;info.NewBackupInfo(realpath);_data->Insert(info);}static std::string GetTimeStr(const time_t time){std::string t = ctime(&time);return t;}static void ListShow(const httplib::Request &req, httplib::Response &rsp){//1.获取所有文件备份信息std::vector<BackupInfo> arry;_data->GetAll(&arry);//2.根据所有备份信息。组织html文件数据std::stringstream ss;ss << "<html><head><title>Download</title></head>";ss << "<body><h1>Download</h1><table>";for(auto& a : arry){ss << "<tr>";std::string filename = FileUtil(a.real_path).FileName();ss << "<td><a href='" << a.url << "'>" << filename << "</a></td>";ss << "<td align='right'>"<< GetTimeStr(a.mtime) << "</td>";ss << "<td align='right'>"<< a.fsize / 1024 << "k</td>";ss << "</tr>";}ss << "</table></body></html>";rsp.body = ss.str();//设置响应报头rsp.set_header("Content-Type", "text.html");rsp.status = 200;}static std::string GetEtag(const BackupInfo& info){//etag = filename + fsize + mtime;FileUtil fu(info.real_path);std::string etag = fu.FileName();etag += "-" + std::to_string(info.fsize);etag += "-" + std::to_string(info.mtime);return etag;}static void Download(const httplib::Request &req, httplib::Response &rsp){//1.获取客户端请求的资源路径path ---> req.path//2.根据资源路径获取,获取文件备份信息BackupInfo info;_data->GetOneByURL(req.path, &info);//3.判断文件是否被压缩,如果被压缩则解压if(info.pack_flag ==true){//解压缩FileUtil fu(info.pack_path);fu.UnCompress(info.real_path);//删除压缩包fu.Remeove();//修改是否被压缩的标志位info.pack_flag = false;//更新文件属性_data->Updata(info);}//4.读取文件数据放入到rsp.body中FileUtil fu(info.real_path);fu.GetContent(&rsp.body);//6.设置响应头部字段:Etag, Accept-Ranges: bytesrsp.set_header("Accept-Ranges", "bytes");rsp.set_header("ETag", GetEtag(info));rsp.set_header("Content-Type", "application/octet-stream");rsp.status = 200;}public:Service(){Config* config = Config::GetInstance();_server_port = config->GetServerPort();_server_ip = config->GetServerIp();_download_prefix = config->GetDownloadPrefix();}bool RunModule(){_server.Post("/upload", Upload);_server.Get("/listshow",ListShow);_server.Get("/", ListShow);std::string download_url = _download_prefix + "(.*)";_server.Get(download_url, Download);_server.listen("0.0.0.0", _server_port);return true;    }private:int _server_port;std::string _server_ip;std::string _download_prefix;httplib::Server _server;};
}#endif

断点续传:

功能:

​ 当文件下载过程中,因为某种异常而断,如果再次进行从头下载,效率较低,因为需要将之前已经传输过的数据再次传输一遍,因此断点续传就是从上次下载断开的位置,重新下载即可,之前已经传输过的数据将不需要在重新传输。

目的:

​ 提高文件重新传输效率
实现思想:

​ 数据写入文件后记录自己当前下载的数据量。客户端在下载文件的时候,要每次接收至当异常下载中断时,下次断点续传的时候将要重新下载的数据区间(下载起始位置,结束位置)发送给服务器,服务器收到后,仅回传客户端需要的区间数据即可需要考虑的问题:如果上次下载文件之后,这个文件在服务器上被修改了,则这时候将不能重新断点续传,而是应该重新进行于文件下载操作。

在http协议中断点续传的实现:

主要关键点:

​ 1.在于能够告诉服务器下载区间

​ 2.服务器上要能够检测上一次下载之后这个文件是否被修改过

实现:

image-20240422121659575

实现:

在download函数的基础上去添加:

static void Download(const httplib::Request &req, httplib::Response &rsp){//1.获取客户端请求的资源路径path ---> req.path//2.根据资源路径获取,获取文件备份信息BackupInfo info;_data->GetOneByURL(req.path, &info);//3.判断文件是否被压缩,如果被压缩则解压if(info.pack_flag ==true){//解压缩FileUtil fu(info.pack_path);fu.UnCompress(info.real_path);//删除压缩包fu.Remeove();//修改是否被压缩的标志位info.pack_flag = false;//更新文件属性_data->Updata(info);}//4.读取文件数据放入到rsp.body中FileUtil fu(info.real_path);bool retrans = false;std::string old_etag; //原先下载的etag;//判断是否是断点续传if(req.has_header("If-Range")){   old_etag = req.get_header_value("If-Range");//etag一致if(old_etag == GetEtag(info)){retrans = true;}                }//若没有If-Range字段或者有该字段,但是etag不一致,则从头下载if(retrans == false){fu.GetContent(&rsp.body);// 6.设置响应头部字段:Etag, Accept-Ranges: bytesrsp.set_header("Accept-Ranges", "bytes");rsp.set_header("ETag", GetEtag(info));rsp.set_header("Content-Type", "application/octet-stream");rsp.status = 200;}else {//httplib内部实现了对于区间请求的处理//只需要我们用户将文件所有数据读取到rsp.body中,它内部会自动根据请求区间//从body中取出指定区间的数据进行相应//std::string range = req.get_header_val("Range"); //bytes=start-end;fu.GetContent(&rsp.body);rsp.set_header("Accept-Ranges", "bytes");rsp.set_header("ETag", GetEtag(info));rsp.set_header("Content-Type", "application/octet-stream");//rsp.set_header("Content-Range", "bytes start-end/fsize");rsp.status = 206;  //区间下载的响应}}

6.客户端业务处理模块

6.1客户端功能和模块化设计

image-20240422170815794

6.2移植一部分代码到windows下的vs

Util.hpp:

  • 删除了不必要的压缩/解压缩函数
  • 删除了JsonUtil类
#pragma once
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
//这个宏是因为在新版本里vs下已经弃用#include <experimental/filesystem>头文件
//但是我们还是继续使用,所以声明一下,防止警告#include <iostream>
#include <fstream>
#include <vector>
#include <experimental/filesystem>#include <sys/types.h>
#include <sys/stat.h>namespace cloud
{namespace fs = std::experimental::filesystem;class FileUtil{public:FileUtil(const std::string& filename):_filename(filename){}// 删除文件bool Remeove(){if (this->Exists() == false){return true;}remove(_filename.c_str());return true;}// 1.获取文件大小size_t FileSize(){struct stat st;if (stat(_filename.c_str(), &st) < 0){std::cout << "Got file size failed!\n";return 0;}return st.st_size;}// 获取文件最后一次修改时间                                        time_t LastMTime(){struct stat st;if (stat(_filename.c_str(), &st) < 0){std::cout << "Got file size failed!\n";return -1;}return st.st_mtime;}// 获取文件最后一次访问时间                                    time_t LastATime(){struct stat st;if (stat(_filename.c_str(), &st) < 0){std::cout << "Got file size failed!\n";return -1;}return st.st_atime;}// 获取文件的名称 /abc/test.txt -> test.txt                           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 = this->FileSize();if (pos + len > fsize){std::cout << "get file len is error\n";return false;}std::ifstream ifs;ifs.open(_filename, std::ios::binary);if (ifs.is_open() == false){std::cout << "read open file failed\n";return false;}ifs.seekg(pos, std::ios::beg);body->resize(len);ifs.read(&(*body)[0], len);if (ifs.good() == false){std::cout << "read file is failed\n";ifs.close();return false;}ifs.close();return true;}// 从文件中读取数据bool GetContent(std::string* body){size_t fsize = this->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 error\n";return false;}ofs.write(&body[0], body.size());if (ofs.good() == false){std::cout << "write file error\n";ofs.close();return false;}ofs.close();return true;}// 判断文件是否存在bool Exists(){return fs::exists(_filename);}// 创建目录                                              bool CreateDirectory(){if (this->Exists()) return true;else return fs::create_directories(_filename);}// 浏览获取目录下所有文件路径名                        bool ScanDirectory(std::vector<std::string>* arry){for (auto& p : fs::directory_iterator(_filename)){if (fs::is_directory(p) == true){continue;}//relative.path带有路径的文件名arry->push_back(fs::path(p).relative_path().string());}return true;}private:std::string _filename;};}

6.3数据管理类的设计

这个类 Datamanager 用于管理备份信息,它主要负责在文件系统中存储和检索数据。以下是其主要功能和设计要点:

  1. 备份文件的管理

    • Datamanager 类通过 _backupFile 成员变量维护一个备份文件的路径。
    • 它负责在对象初始化时加载备份文件的内容,并在对象销毁时将数据存储到备份文件中。
  2. 数据的存储与检索

    • Datamanager 使用一个 std::unordered_map<std::string, std::string> 作为内部数据结构,用于存储键值对信息。键通常代表数据的唯一标识符,值可以是相关的元数据或文件路径等。
    • 提供了插入、更新和检索的方法,如 InsertUpDataGetOneByKey
  3. 文件持久化和初始化

    • 在对象构造时,InitLoad 方法从备份文件中加载数据,解析后存入 _table
    • 在对象析构时,Storage 方法将 _table 中的数据保存到备份文件中,确保持久化。
    • Split 方法用于将字符串按特定分隔符进行分割,为数据加载和解析提供支持。
#ifndef __MY_DATA__
#define __MY_DATA__#include "Util.hpp"
#include <unordered_map>
#include <sstream>namespace cloud
{class Datamanager{public:Datamanager(const std::string& backup_file):_backup_file(backup_file){InitLoad();}~Datamanager(){Storage();}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;}int Split(const std::string& body, const std::string& sep, std::vector<std::string>* arry){int count = 0;size_t pos = 0, idx = 0;while ((pos = body.find(sep, idx)) != std::string::npos){std::string tmp = body.substr(idx, pos - idx);arry->push_back(tmp);count++;idx = pos + sep.size();}// 如果最后一段不是空的,才添加到数组if (idx < body.size()){std::string tmp = body.substr(idx);arry->push_back(tmp);count++;}return count;}bool InitLoad(){//1.获取备份信息FileUtil fu(_backup_file);std::string body;fu.GetContent(&body);//2.按照格式分割std::vector<std::string> arry;Split(body, "\n", &arry);//3.将分割后的结果插入到_table中for (auto& a : arry){//b.txt b.txt-34567-345636std::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;return true;}bool UpData(const std::string& key, const std::string& val){_table[key] = val;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:std::string _backup_file; //备份信息的持久化文件std::unordered_map<std::string, std::string> _table; //存储文件唯一标识符:文件路径+唯一标识};
}#endif 

6.4文件备份类的设计

image-20240422205547856

实现:

#ifndef __MY_CLOUD__
#define __MY_CLOUD__#define SERVER_ADDR "117.72.37.100"
#define SERVER_PORT 9090#include "data.hpp"
#include "httplib.h"#include <windows.h>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(const std::string filename){//b.txt-b.size-b.mtimeFileUtil 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.name = "file";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 RunMudule(){while (1){//1.遍历获取指定文件夹中的所有文件//2.逐个判断文件是否需要上传//3.如果需要上传则上传文件FileUtil fu(_back_dir);std::vector <std::string> arry;fu.ScanDirectory(&arry);for (auto& a : arry){if (IsNeedUpLoad(a) == false){continue;}if (UpLoad(a) == true){_data->Insert(a, GetFileIdentifier(a));}}Sleep(1);_data->Storage();}}private:std::string _back_dir; //备份文件夹Datamanager* _data;  //数据管理对象};}#endif 

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

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

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

相关文章

【电控笔记5.6】Butterworth滤波器

Butterworth滤波器 需求&#xff1a;在增益交越频率拥有最小的相位滞后 波器经常被使用原因是 Butterworth 滤波器对于给定阶数&#xff0c;拥有最倾斜的衰减率而在伯德图又不会产生凸峰&#xff0c;同时在低频段的相位滞后小&#xff0c;因此本节将为各位介绍 Butterworth 低…

CTFshow-PWN-栈溢出(pwn43)

32位的 system(); 但是好像没"/bin/sh" 上面的办法不行了&#xff0c;想想办法 检查&#xff1a;32 位程序 ida 分析&#xff1a; 跟进 ctfshow 函数 定义了一个长度为 104 的字符数组 s&#xff0c;gets() 函数被用来从标准输入&#xff08;键盘&#xff09;中读取…

OpenCompass 大模型评测实战——笔记

OpenCompass 大模型评测实战——笔记 一、评测1.1、为什么要做评测1.2、如何通过能力评测促进模型发展1.2.1、面向未来拓展能力维度1.2.2、扎根通用能力1.2.3、高质量1.2.4、性能评测 1.3、评测的挑战1.3.1、全面性1.3.2、评测成本1.3.3、数据污染1.3.4、鲁棒性 二、OpenCompas…

【漏洞复现】云时空社会化商业ERP系统LoginName SQL注入漏洞

漏洞描述&#xff1a; 云时空社会化商业ERP系统loginName存在SQL注入漏洞&#xff0c;攻击者可以通过此漏洞获取数据库敏感信息。 搜索语法: Fofa-Query: app"云时空社会化商业ERP系统" 漏洞详情&#xff1a; 1.云时空社会化商业ERP系统。 2.漏洞POC&#xff1a…

迪拜Token2049展会圆满落幕,MVP成唯一MEMECOIN项目,闪耀全场!

近日&#xff0c;据多家媒体报道&#xff0c;于全球财富聚集地迪拜举行的全球性大型区块链会议TOKEN2049圆满落幕。来自全球的5000多家公司和100多个国家10000名参与者共同参会&#xff0c;讨论未来30年至50年关于区块链行业的宏大未来。 新晋MEMECOIN项目MAGA VP&#xff08;…

【NLP】大语言模型基础之GPT

大语言模型基础之GPT GPT简介1. 无监督预训练2. 有监督下游任务微调 GPT-4体系结构1. GPT-4的模型结构2. GPT-4并行策略3. GPT-4中的专家并行GPT-4的特点 参考连接 以ELMo为代表的动态词向量模型开启了语言模型预训练的大门&#xff0c;此后&#xff0c;出现了以GPT和BERT为代表…

Spring - 3 ( 12000 字 Spring 入门级教程 )

一&#xff1a;Spring Web MVC入门 1.1 响应 在我们前⾯的代码例子中&#xff0c;都已经设置了响应数据, Http 响应结果可以是数据, 也可以是静态页面&#xff0c;也可以针对响应设置状态码, Header 信息等. 1.2 返回静态页面 创建前端页面 index.html(注意路径) html代码 …

SpringMVC基础篇(二)

文章目录 1.Postman1.基本介绍Postman是什么&#xff1f; 2.Postman快速入门1.Postman下载点击安装自动安装在系统盘 2.基本操作1.修改字体大小2.ctrl “” 放大页面3.进入创建请求界面 2.需求分析3.具体操作4.保存请求到文件夹中1.点击保存2.创建新的文件夹3.保存成功 3.使用…

嵌入式4-24

作业&#xff1a; 整理思维导图 定义一个矩形类Rec&#xff0c;包含私有属性length&#xff0c;width&#xff0c;有以下成员函数&#xff1a; void set_length(int l); //设置长度 void set_width(int w); //设置宽度 int get_length(); //获取长度 int get_width(); //获取宽…

【上海大学计算机组成原理实验报告】四、指令系统实验

一、实验目的 了解指令结构、PC寄存器的功能和指令系统的基本工作原理。 学习设计指令的方法。 二、实验原理 根据实验指导书的相关内容&#xff0c;对于部分使用频率很高&#xff0c;且只用几条微指令即可完成的简单操作&#xff0c;可以把这部分简单操作的微指令序列固定下…

C#窗体中动态按钮的设计方法:创建特殊窗体

目录 1.动态按钮的设计方法 2.实例 &#xff08;1&#xff09; Resources.Designer.cs &#xff08;2&#xff09;Form1.Designer.cs &#xff08;3&#xff09;Form1.cs &#xff08;4&#xff09; 生成效果 在窗体界面中&#xff0c;通常以按钮来代替菜单栏的功能&…

华卓荣登「2024数商典型应用场景“乘数榜”」

4月18日&#xff0c;2024未来数商大会在杭州未来科技城学术交流中心举行&#xff0c;由浙江省科学技术协会指导&#xff0c;未来数商大会组委会主办&#xff0c;浙江省数字经济学会、国脉研究院承办。中国工程院院士陈纯、中国互联网协会副理事长高新民、中国社科院信息化研究中…

网工内推 | 深圳网工专场,上市公司、国企,安全认证优先

01 深圳市同为数码科技股份有限公司武汉分公司 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、负责网络设备的管理、调试、配置、维护等&#xff1b; 2、负责信息安全网络安全设备、系统的运维&#xff1b; 3、负责整体网络系统技术的相关工作&#xff0c;包括架构…

python学习笔记(集合)

知识点思维导图 # 直接使用{}进行创建 s{10,20,30,40} print(s)# 使用内置函数set()创建 sset() print(s)# 创建一个空的{}默认是字典类型 s{} print(s,type(s))sset(helloworld) print(s) sset([10,20,30]) print(s) s1set(range(1,10)) print(s1)print(max:,max(s1)) print(m…

Docker的介绍及应用

1.什么是Docker 我们在部署大型项目的时候&#xff0c;肯定会遇到这种问题&#xff0c;大学项目组件较多&#xff0c;运行环境复杂&#xff0c;部署时会碰到一些问题&#xff1a;例如node、redis、mysql等这些应用都有自己的依赖和函数库。这种复杂的依赖关系很容易出现兼容问…

【docker】

下载脚本 yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine docker-ce yum install -y yum-utils device-mapper-persistent-data lvm2 --s…

Redis概述

目录 redis概述1. 什么是redis2. 为什么使用redis3. redis为什么快4. 哪些场景下使用redis 参考 Redis概述 Redis在线体验网站 Try Redis 基础概念&#xff0c;后续知识有时间整理&#xff08;数据结构、核心知识、高可用&可扩展、应用实践&#xff09; redis概述 1. 什么是…

YOLOv9改进策略 | 添加注意力篇 | TripletAttention三重注意力机制(附代码+机制原理+添加教程)

一、本文介绍 本文给大家带来的改进是Triplet Attention三重注意力机制。这个机制&#xff0c;它通过三个不同的视角来分析输入的数据&#xff0c;就好比三个人从不同的角度来观察同一幅画&#xff0c;然后共同决定哪些部分最值得注意。三重注意力机制的主要思想是在网络中引入…

Mac中隐私安全性设置-打开任何来源

文章目录 **Mac中隐私安全性设置-打开任何来源**一、目的二、打开方式 Mac中隐私安全性设置-打开任何来源 一、目的 从外部下载的软件频繁打不开&#xff0c;需要从隐私安全性中重新选择一下&#xff1b;默认Mac隐藏了任何来源 二、打开方式 打开终端&#xff0c;输入一下命…

51.HarmonyOS鸿蒙系统 App(ArkUI)通知

普通文本通知测试 长文本通知测试 多行文本通知测试 图片通知测试 进度条通知测试 通知简介 应用可以通过通知接口发送通知消息&#xff0c;终端用户可以通过通知栏查看通知内容&#xff0c;也可以点击通知来打开应用。 通知常见的使用场景&#xff1a; 显示接收到的短消息、…