您的位置:首页 > 房产 > 建筑 > 项目实现:云备份③(配置文件加载模块、数据管理模块的实现)

项目实现:云备份③(配置文件加载模块、数据管理模块的实现)

2024/12/27 19:05:26 来源:https://blog.csdn.net/weixin_57663528/article/details/142250870  浏览:    关键词:项目实现:云备份③(配置文件加载模块、数据管理模块的实现)

云备份

  • 前言
  • 配置文件加载模块
    • 配置信息的设计
    • 单例文件配置类设计
  • 数据管理模块
    • 数据信息类设计
    • 数据管理类实现
    • 数据持久化存储
    • 初始化加载实现

前言

书接上回:云备份(实用工具类)实现后,接下来会逐步实现不同模块的功能。会频繁用到工具类,如果不熟悉的老铁还是先看看前面的内容,对接下来要讲解的模块实现会有比较好理解。

配置文件加载模块

配置信息的设计

  1. 热点时间判断:设定一个合适的时间,决定在这个时间过后的文件是非热点文件。对于非热点文件进行压缩存储。
  2. 文件下载 URL 前缀路径:通常这个路径是用来判断用户的下载请求,可能是下载文件请求,也可能是查看备份文件列表的请求。
  3. 压缩文件的后缀名:在这里默认将压缩后的文件后缀设置为 .lz
  4. 上传文件的存放路径:决定了用户上传文件的存储到服务器的哪里。
  5. 压缩包存放位置:对于非热点文件、压缩包 和 热点文件、普通文件要区分存储。决定了非热点文件、压缩文件的存储位置。
  6. 服务器备份信息存储到文件:记录备份文件信息,保证数据的持久化
  7. 服务器的监听IP、监听端口:当客户端运行到其他主机时,不再需要修改程序

单例文件配置类设计

这里的单例模式采用懒汉模式,对饿汉、懒汉单例模式的实现可以参考小编的这篇文章:特殊类设计

单例文件配置类是用来配置信息的,其中主要实现以下几个功能:

  • GetHotTime:提供热点判断时间
  • GetServerPort:提供服务器监听端口
  • GetServerIP:提供服务器监听IP
  • GetDownloadPrefix:提供下载的 URL 前缀路径
  • GetPackFileSuffix:提供压缩包后缀名
  • GetBackDir:提供备份文件的存放目录
  • GetPackDir:提供压缩包存放的目录
  • GetBackupFile:提供信息存储的文件

下面开始进行代码的编写。

  1. 首先创建 cloud.conf 配置文件,编写配置信息。使用的是Json格式进行编写:
{"hot_time" : 30,"server_port" : 9090,"server_ip" : "xxx.xxx.xxx.xxx","download_prefix" : "/download/","packfile_suffix" : ".lz","pack_dir" : "./packdir/","back_dir" : "./backdir/","backup_file" : "./cloud.dat"
}

这里的服务器 IP 设置按照个人需求进行改写,当然端口号也是。

  1. 单例文件配置类的实现,创建 config.hpp 文件进行代码的编写:
#ifndef CLOUD_HPP
#define CLOUD_HPP#include <mutex>
#include "util.hpp"namespace cloud
{
#define CONFIG_FILE "./cloud.conf"class Config{public:// 获取单例对象指针static Config *GetInstance(){// 双检查加锁if (_instance == nullptr){// 加锁_mutex.lock();if (_instance == nullptr){return _instance = new Config();}// 解锁_mutex.unlock();}}//提供热点判断时间int GetHotTime() { return _hot_time; }//提供服务器监听端口int GetServerPort() { return _server_port; }//提供服务器监听IPstd::string GetServerIP() { return _server_ip; }//提供下载的URL前缀路径 std::string GetDownloadPrefix() { return _download_prefix; }//提供压缩包后缀名std::string GetPackFileSuffix() { return _packfile_suffix; }//提供备份文件的存放目录std::string GetBackDir() { return _back_dir; }//提供压缩包存放的目录std::string GetPackDir() { return _pack_dir; }//提供信息存储的文件std::string GetBackupFile() { return _backup_file; }private:// 获取cloud.conf文件信息,进行初始化bool ReadConfigFile(){cloud::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();_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;}int _hot_time;                // 热点时间判断int _server_port;             // 服务器监听端口std::string _server_ip;       // 服务器监听IPstd::string _download_prefix; // 文件下载的URL前缀路径std::string _packfile_suffix; // 压缩文件的后缀名std::string _pack_dir;        // 压缩包存放目录std::string _back_dir;        // 备份文件的存放目录std::string _backup_file;     // 提供信息存储的文件private:Config(){ReadConfigFile();}// 防止单例的拷贝与赋值Config(const Config &config) = delete;Config &operator=(const Config &config) = delete;static Config *_instance;static std::mutex _mutex;};// 静态成员的初始化:类外进行Config *Config::_instance = nullptr;std::mutex Config::_mutex;
}
#endif

测试代码:

#pragma once
#include "util.hpp"
#include "config.hpp"
void ConfigTest()
{// 实例化单例cloud::Config *config = cloud::Config::GetInstance();std::cout << config->GetHotTime() << std::endl;std::cout << config->GetServerPort() << std::endl;std::cout << config->GetServerIP() << std::endl;std::cout << config->GetDownloadPrefix() << std::endl;std::cout << config->GetPackFileSuffix() << std::endl;std::cout << config->GetBackDir() << std::endl;std::cout << config->GetPackDir() << std::endl;std::cout << config->GetBackupFile() << std::endl;
}
int main(int argc, char *argv[])
{ConfigTest();return 0;
}

实现效果:
在这里插入图片描述
可以看到,这里将 cloud.conf 文件内容进行了正常输出,初始化也没有问题。

数据管理模块

好的管理模式,在后期可以帮助我们去更好的处理大量的数据。

为了后期更好的管理数据,并且使用这些数据,首先要对文件的以下这些数据信息进行封装管理:

  1. 文件的实际存储路径:客户端要下载文件时,从这个文件中读取数据进行响应。
  2. 文件压缩包存放路径:对于非热点文件会被压缩处理,压缩包的路径也是需要管理的。当用户需要这个文件时,要先对压缩包进行解压,让后再读取响应的内容。
  3. 文件是否压缩的标志位:用于判断文件是否压缩
  4. 文件大小
  5. 文件最后一次访问时间
  6. 文件最后一次修改时间
  7. 文件访问URL中的资源路径path:/download/a.txt

数据信息类设计

在 src 文件下创建 data.hpp 文件,对 BackupInfo 数据信息类进行代码编写。

  • 实现 BackupInfo 数据信息类

BackupInfo 主要功能是对数据信息进行封装,实现如下:

#include "config.hpp"namespace cloud
{struct BackupInfo{bool pack_flag;        // 压缩文件的标志位size_t fsize;          // 文件大小time_t mtime;          // 文件最后一次修改时间time_t atime;          // 文件最后一次访问时间std::string real_path; // 文件的实际存储路径std::string pack_path; // 文件压缩包存放路径std::string url;       // URL中的资源路径path//初始化信息bool NewBackupInfo(const std::string& realpath){FileUtil fu(realpath);if(fu.Exists() == false){std::cout << "new backupbnfo : file is not exists!\n";return false;}Config* config = Config::GetInstance();//获取配置文件信息std::string packdir = config->GetPackDir();std::string packsuffix = config->GetPackFileSuffix(); //获取压缩文件后缀std::string download_prefix = config->GetDownloadPrefix(); //文件下载路径pack_flag = false; //初始设置为普通文件//赋值处理fsize = fu.FileSize(); mtime = fu.LastMTime();atime = fu.LastATime();real_path =  realpath; //文件实际所在的路径//./backdir/a.txt -> ./packdir/a.txt.lzpack_path = packdir + fu.FileName() + packsuffix; //压缩包路径//./backdir/a.txt -> /download/a.txturl = download_prefix + fu.FileName(); //文件下载路径return true;}};
}

测试代码:

#include "data.hpp"void DataTest(const std::string &filename)
{cloud::BackupInfo info;info.NewBackupInfo(filename);//输出BackupInfo数据信息std::cout << info.pack_flag << std::endl;std::cout << info.fsize << std::endl;std::cout << info.mtime << std::endl;std::cout << info.atime << std::endl;std::cout << info.real_path << std::endl;std::cout << info.pack_path << std::endl;std::cout << info.url << std::endl;
}
int main(int argc, char *argv[])
{DataTest(argv[1]);return 0;
}

这里直接拿 src 当前目录下的文件进行测试,实现效果:
在这里插入图片描述

数据管理类实现

为了更快的访问数据信息,实现一个数据管理类。

在这里采用 hash 表在内存中管理数据;以 URL 的 path 路径作为key值;拿数据信息类 BackupInfo 对象作为为 val值。

其中,DataManager 数据管理类主要实现以下几个功能:

  1. Insert:支持插入新的数据信息
  2. Update:支持更新数据信息
  3. GetOneByUrl:通过URL资源路径查找对应文件的数据信息
  4. GetOneByRealPath:通过文件路径查找对应的数据信息
  5. GetAll:获取所有的文件数据信息
  • 实现 DataManager 数据管理类

在实现前要讲一点:由于数据可以被多人访问,但是对于写只能允许一个人进行操作。

由此要引入一个 pthread_rwlock_t 读写锁

  • 读写锁 允许多个线程同时读取共享资源,但只允许一个线程进行写操作

下面是 DataManager 类的实现代码:

#include "config.hpp"
#include <unordered_map>
#include <pthread.h>class DataManager
{
public:DataManager(){// 初始化读写锁pthread_rwlock_init(&_rwlock, nullptr);// 将拷贝文件的信息进行初始化_backup_file = Config::GetInstance()->GetBackupFile();}~DataManager(){// 销毁读写锁pthread_rwlock_destroy(&_rwlock);}// 插入数据信息bool Insert(const BackupInfo &info){pthread_rwlock_wrlock(&_rwlock); // 上锁_table[info.url] = info;         // 数据不存在直接进行插入pthread_rwlock_unlock(&_rwlock); // 解锁return true;}// 更新数据信息bool UpDate(const BackupInfo &info){pthread_rwlock_wrlock(&_rwlock); // 上锁_table[info.url] = info;         // 数据不存在直接进行插入,否则更新对应的内容pthread_rwlock_unlock(&_rwlock); // 解锁return true;}// 通过URL的文件资源路径,获取对应的备份文件的数据信息bool GetOneByUrl(const std::string &url, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock); // 上锁auto it = _table.find(url);if (it == _table.end()){// 没有找到pthread_rwlock_unlock(&_rwlock); // 解锁return false;}// 找到了*info = it->second;              // 迭代器的second就是val值pthread_rwlock_unlock(&_rwlock); // 解锁return true;}// 通过文件路径查找备份文件的数据信息bool GetOneByRealPath(const std::string &realpath, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock); // 上锁// 遍历整个_tableauto it = _table.begin();while (it != _table.end()){if (it->second.real_path == realpath){// 文件数据信息匹配到了*info = it->second;pthread_rwlock_unlock(&_rwlock); // 解锁return true;}++it;}// 没有找到pthread_rwlock_unlock(&_rwlock); // 解锁return false;}//获取整个数据信息bool GetAll(std::vector<BackupInfo> *array) //输出型参数{pthread_rwlock_wrlock(&_rwlock); // 上锁//遍历整个_tableauto it = _table.begin();while(it != _table.end()){//_table表中的val值都插入array中array->push_back(it->second);++it;}pthread_rwlock_unlock(&_rwlock); // 解锁return true;}private:pthread_rwlock_t _rwlock;                           // 读写锁std::string _backup_file;                           // 备份文件std::unordered_map<std::string, BackupInfo> _table; // 文件路径,数据信息对象
};

测试代码:

#include "data.hpp"void PrintBackUpInfo(const cloud::BackupInfo &info)
{std::cout << info.pack_flag << std::endl;std::cout << info.fsize << std::endl;std::cout << info.mtime << std::endl;std::cout << info.atime << std::endl;std::cout << info.real_path << std::endl;std::cout << info.pack_path << std::endl;std::cout << info.url << std::endl;
}
void DataTest(const std::string &filename)
{cloud::BackupInfo info;info.NewBackupInfo(filename); // 初始化cloud::DataManager data;std::cout << "------------------Insert------------------------\n";data.Insert(info); // 将备份文件数据信息进行插入cloud::BackupInfo tmp;data.GetOneByUrl("/download/bundle.h", &tmp); // 查看下载目录下是否存在bundle.h文件// 打印对应的数据信息PrintBackUpInfo(tmp);std::cout << "------------------UpDate------------------------\n";// 修改刚刚插入数据的内容info.pack_flag = true;data.UpDate(info);std::vector<cloud::BackupInfo> array;data.GetAll(&array);for (auto &e : array){PrintBackUpInfo(e);}std::cout << "------------------GetOneByRealPath------------------------\n";data.GetOneByRealPath(filename, &tmp);PrintBackUpInfo(tmp);
}
int main(int argc, char *argv[])
{DataTest(argv[1]);return 0;
}

在这里还是以 src 目录下的 bundle.h 文件为测试案例,测试结果如下:
在这里插入图片描述

数据持久化存储

为了持久化存储管理,采用Json序列化将所有的数据保存在文件中。

完善 DataManager 类,实现 Storage 成员函数。实现步骤如下:

  1. 获取 _table 中所有数据
  2. 将数据添加到 Json::Value 对象中
  3. 对 Json::Value 对象进行序列化操作
  4. 将序列化数据填写到文件

下面只展示当前功能代码,如下:

class DataManager
{
public://...其他成员函数// 数据持久化操作bool Storage(){// 1.获取所有的数据信息std::vector<BackupInfo> array;GetAll(&array);// 2.将获取的数据添加到Json::Value对象Json::Value root;for (int i = 0; i < array.size(); i++){Json::Value tmp;tmp["pack_flag"] = array[i].pack_flag;// Json不认识 time_t、size_t类型,需要强制类型转换tmp["fsize"] = (Json::Int64)array[i].fsize;tmp["mtime"] = (Json::Int64)array[i].mtime;tmp["atime"] = (Json::Int64)array[i].atime;tmp["real_path"] = array[i].real_path;tmp["pack_path"] = array[i].pack_path;tmp["url"] = array[i].url;// 整个数据不仅仅只有一份root[i].append(tmp);}// 3.序列化操作std::string body;if(JsonUtil::Serialize(root, &body) == false){std::cout << "Data write failed!\n";return false;}// 4.将数据存储到文件FileUtil fu(_backup_file); // 写入到备份文件fu.SetContent(body);return true;}
private:pthread_rwlock_t _rwlock;                           // 读写锁std::string _backup_file;                           // 备份文件std::unordered_map<std::string, BackupInfo> _table; // 文件路径,数据信息对象
};

数据持久化操作,常用于新的数据插入后、数据更新后。

对此,需要对先前的 插入数据、更新数据的函数增添数据持久化功能,下面是 Insert、UpDate 函数修改后的代码:

// 插入数据信息
bool Insert(const BackupInfo &info)
{pthread_rwlock_wrlock(&_rwlock); // 上锁_table[info.url] = info;         // 数据不存在直接进行插入pthread_rwlock_unlock(&_rwlock); // 解锁//数据持久化Storage();return true;
}
// 更新数据信息
bool UpDate(const BackupInfo &info)
{pthread_rwlock_wrlock(&_rwlock); // 上锁_table[info.url] = info;         // 数据不存在直接进行插入,否则更新对应的内容pthread_rwlock_unlock(&_rwlock); // 解锁//数据持久化Storage();return true;
}

初始化加载实现

初始化程序运行时,从文件读取数据。

完善 DataManager 类,实现 InitLoad 成员函数。实现步骤如下:

  1. 将数据从文件中读取出来
  2. 反序列化操作
  3. 反序列化得到的数据添加到 _table 中

下面只展示当前功能代码,如下:

class DataManager
{
public://...其他成员函数//数据初始化加载bool InitLoad(){//1.将数据从文件中读取出来FileUtil fu(_backup_file);if(fu.Exists() == false){//文件不存在std::cout << "The back file does not exist\n";return false;}std::string body;fu.GetContent(&body);//2.反序列化Json::Value root;if(JsonUtil::UnSerialize(body, &root) == false){std::cout << "Deserialization failed\n";return false;}//3.将反序列化的数据添加到_tablefor(int i = 0; i < root.size(); i++){BackupInfo info;info.pack_flag = root[i]["pack_flag"].asBool();info.fsize = root[i]["fsize"].asInt64();info.mtime = root[i]["mtime"].asInt64();info.atime = root[i]["atime"].asInt64();info.pack_path = root[i]["pack_path"].asString();info.real_path = root[i]["real_path"].asString();info.url = root[i]["url"].asString();//将info添加到_table中Insert(info);}return true;}
private:pthread_rwlock_t _rwlock;                           // 读写锁std::string _backup_file;                           // 备份文件std::unordered_map<std::string, BackupInfo> _table; // 文件路径,数据信息对象
};

初始化一般都会在构造函数内部进行。实现初始化信息加载后,需要对构造函数进行功能添加:

 DataManager(){// 初始化读写锁pthread_rwlock_init(&_rwlock, nullptr);// 将拷贝文件的信息进行初始化_backup_file = Config::GetInstance()->GetBackupFile();//初始化加载InitLoad();}

到这里,数据管理模块的功能基本实现。

该篇文章主要实现了云备份项目的两个功能模块,由于篇幅过长,剩下模块的内容会在后续博客中体现。喜欢的老铁可以点赞 + 收藏!你们的关注是我持续更新的动力,感谢大家的观看。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com