【云备份】配置文件加载模块

目录

 一.为什么要配置文件

二.配置文件的实现

三.单例文件配置类设计

四.源码


 

 一.为什么要配置文件

我们将服务端程序运行中用到的一些关键信息保存到配置文件中,这样可以使程序的运行更加灵活。

这样做的好处是,未来如果我们想要修改一些关键信息,不需要去源文件里修改,避免了文件重新编译等。

配置文件信息

  • 热点判断时间

决定热点文件隔多长时间会被判定为非热点文件。这个必须是可变的,因为我们

  • 文件下载的 URL 前缀路径

用于表示客户端的请求是一个下载请求。

一般来说,我们的用户都是输入类似于下面这种URL:http://192.168.122.136:9090/path,通过这样子的方式,默认访问到的是我们根目录下的path文件了吗?那访问我们服务器的用户不都可以通过这种方式获取我服务器上的所有文件了吗?这是很危险的,所以我们会在程序运行时添加一个新的相对的根目录。例如wwwroot。此时用户只能访问/wwwroot/里面的所有文件,这就是一种前缀路径。

示例:但是当用户发一个请求listshow来查看备份列表,listshow可能是一种指令,也可能是一个文件的名字,那我们如何判断这个请求不是一个listshow的文件下载请求呢?

此时我们可以为下载请求添加一个前缀路径,例如/download/listshow,那么就认为它是一个下载一个名字叫listshow文件的请求

  • 压缩包后缀名

约定一个压缩包命名规则。示例:在原文件后面加上 .lz 表示该文件的压缩包名称。

  • 上传文件存放路径

决定上传文件之后,该文件实际存储在服务器的何处。

  • 压缩包存放路径

决定压缩后的文件存储在何处。

上传的文件也可能是压缩文件,而我们对于上传的文件里面对于非热点文件,我们会进行压缩,如果非热点文件是压缩文件,那我们不能对其再次进行压缩。所以要将压缩包和普通文件分开来。

  • 服务端备份信息存放文件

服务端记录的备份文件信息的持久化存储路径或文件名。

事实上,我们这个项目并没有使用数据库来存储任何信息,因为这个是一个入门项目,如果加上数据库,就会显得这个项目有点庞大了。所以我们不加上数据库好了。

当程序需要运行在其他主机上,则不需要修改程序,只需要修改一下配置文件就OK了。

  • 服务器访问 IP 地址

服务器的 IP 地址,用于客户端访问。

当程序需要运行在其他主机上,则不需要修改程序,只需要修改一下配置文件就OK了。

  • 服务器访问端口

服务器的访问端口,用于客户端与服务端之间的通信。

当程序需要运行在其他主机上,则不需要修改程序,只需要修改一下配置文件就OK了。


二.配置文件的实现

我们这些配置文件都是基于JSON来描述的

cloud.conf

// cloud.conf
{"hot_time" : 30,"server_port" : 9090,"server_ip" : "117.72.80.239","download_prefix" : "/download/","packfile_suffix" : ".lz","pack_dir" : "./packdir/","back_dir" : "./backdir/","backup_file" : "./cloud.dat"
}

我们去我们的服务器里,创建一个cloud.conf文件

 

三.单例文件配置类设计

使用单例模式管理系统配置信息,能够让配置信息的管理控制更加统一灵活。该类的设计我们将使用单例模式中的懒汉模式,即在使用时创建对象。

conf.hpp

#ifndef _MY_CONFIG_
#define _MY_CONFIG_
#include"util.hpp"
#include<mutex>namespace cloud {#define CONFIG_FILE "./cloud.conf" // 配置文件路径class Config{public:static Config* GetInstance(){if (_instance == nullptr)//如果还没有被初始化,说明还没有被构造过{_mutex.lock();//加锁保护if (_instance == nullptr)//二次检测{_instance = new Config();//构建新对象}_mutex.unlock();//解锁}return _instance;//返回句柄}int GetHotTime(){return _hot_time;}int GetServerPort(){return _server_port;}std::string GetSeverIp(){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:Config() // 构造函数私有化,因为我们是根据配置文件来对这个类来构造的,所以一个类只能有一个构造函数//无法在类外进行实例化{ReadConfigFile();//一构造就去读取配置文件}bool ReadConfigFile()//读取配置文件{FileUtil fu(CONFIG_FILE);//这个CONFIG_FILE在上面定义了std::string body;if(fu.GetContent(&body) == false)//body存储了配置文件的内容,但配置文件是序列化之后的内容,不能直接使用{std::cout << "load config file failed!" << std::endl;return false;}Json::Value root;if(JsonUtil::Unserialize(body, &root) == false)//root里面存储了配置文件反序列化后的内容{std::cout << "parse config file failed!" << std::endl;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();}private:int _hot_time;//热点判断时间int _server_port;//服务器监听端口号std::string _server_ip;//服务器监听的IPstd::string _download_prefix;//下载的url的前缀路径std::string _packfile_suffix;//压缩文件的后缀名,比如.lzstd::string _pack_dir;// 压缩包存放路径std::string _back_dir;// 备份文件存放路径std::string _backup_file;// 备份信息存放文件static Config* _instance;//句柄,单例模式,只有一个实例,所以加staicstatic std::mutex _mutex;//由于是单例模式的懒汉模式,涉及线程安全,所以需要加互斥锁};Config* Config::_instance = nullptr;//初始化std::mutex Config::_mutex;//锁
};
#endif

我们回到我们的服务器上面来,创建一个conf.hpp,把上面那个代码给填进去就OK了。

我们来对它进行检测一下,

cloud.cc

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

这个时候我们编译,就会发现一个错误

这个是因为util.hpp是被重复包括了,因为cloud.cc和conf.hpp里面都包含了头文件util.hpp,所以我们需要加上防止头文件重复定义的.

我们需要在util.hpp里面加上这个

#ifndef _MY_UTIL_
#define _MY_UTIL_...#endif

现在编译就没有问题了,

我们运行一下

也是一点问题都没有。


到这里,我们还是需要使用git来提交一番

四.源码

util.hpp

#ifndef _MY_UTIL_
#define _MY_UTIL_
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include"bundle.h"
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>namespace cloud {//注意:我们下面这些接口的名称可能会与系统的那些接口的名字重复,所以最好创建名称空间来避免名称污染namespace fs = std::experimental::filesystem;class FileUtil{public:FileUtil(const std::string& filename):_filename(filename){}//主要针对普通文件的接口int64_t FileSize() // 获取文件大小,这里使用64位的有符号的int,防止文件过大导致文件大小显示异常{struct stat st;int re=stat(_filename.c_str(),&st);if(re<0)//stat函数获取文件属性失败了{std::cout<<"Get FileSize Failed!!"<<std::endl;return -1;}return st.st_size;}time_t LastModtime() // 获取文件最后一次修改时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastModtime Failed!!" << std::endl;return -1;}return st.st_mtim.tv_sec;}time_t LastAcctime() // 获取文件最后一次访问时间{struct stat st;int re = stat(_filename.c_str(), &st);if (re < 0)//stat函数获取文件属性失败了{std::cout << "Get LastAcctime Failed!!" << std::endl;return -1;}return st.st_atim.tv_sec;}std::string FileName() // 获取文件路径中的纯文件名称 a/b/c/test.cc --> test.cc{size_t pos=_filename.find_last_of("/");//寻找最后一个/if(pos==std::string::npos)//没找到,说明没有/{return _filename;}return _filename.substr(pos+1);//从pos截取到末尾}bool GetPosLen(std::string* body, size_t pos, size_t len){std::ifstream ifs;ifs.open(_filename, std::ios::binary);//打开文件,以二进制方式来读取数据if (ifs.is_open() == false)//打开失败{std::cout << "GetPosLen: open file failed!" << std::endl;return false;}size_t fsize = this->FileSize();//获取文件大小if (pos + len > fsize)//超过文件大小了{std::cout << "GetPosLen: get file len error" << std::endl;return false;}ifs.seekg(pos, std::ios::beg); // 将文件指针定位到pos处body->resize(len);//把存储读取的数据的载体的大小修改到够大的ifs.read(&(*body)[0], len);//读取数据if (ifs.good() == false)//上次读取出错了{std::cout << "GetPosLen: 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 << "SetContent: write open file failed" << std::endl;return false;}ofs.write(&body[0], body.size());if (ofs.good() == false)//上次写入文件数据出错了{std::cout << "SetContent: write open file failed" << std::endl;ofs.close();return false;}ofs.close();return true;}bool Compress(const std::string& packname){// 1.获取源文件数据std::string body;if (this->GetContent(&body) == false)//源文件数据都存储在body里面{//获取源文件数据失败std::cout << "compress get file content failed" << std::endl;return false;}// 2.对数据进行压缩std::string packed = bundle::pack(bundle::LZIP, body);// 3.将压缩的数据存储到压缩包文件中FileUtil fu(packname);if (fu.SetContent(packed) == false){//压缩数据写入压缩包文件失败std::cout << "compress write packed data failed!" << std::endl;return false;}return true;}bool UnCompress(const std::string& filename){// 1.将当前压缩包数据读取出来std::string body;if (this->GetContent(&body) == false){std::cout << "Uncompress get file content failed!" << std::endl;return false;}// 2.对压缩的数据进行解压缩std::string unpacked = bundle::unpack(body);// 3.将解压缩的数据写入到新文件中FileUtil fu(filename);if (fu.SetContent(unpacked) == false){std::cout << "Uncompress write packed data failed!" << std::endl;return false;}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){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());//添加文件//注意迭代器返回的p不是string,不能直接将p添加进array里面,我们需要使用path将其}return true;}private:std::string _filename; // 文件名--包含路径};class JsonUtil {public:/*** @brief 将 Json::Value 对象序列化为字符串* @param root 输入的 JSON 数据结构(待序列化)* @param str 输出的序列化后的字符串* @return true 序列化成功,false 序列化失败*/static bool Serialize(const Json::Value &root, std::string *str) {// 1. 创建 JSON 流写入器构建器(可配置格式化选项,如缩进)Json::StreamWriterBuilder swb;// 2. 通过构建器生成 StreamWriter 对象(unique_ptr 自动管理内存)std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss;  // 用于存储序列化结果// 3. 将 JSON 数据写入流// 返回值 0 表示成功,非 0 表示失败(JsonCpp 的约定)if (sw->write(root, &ss) != 0) {std::cout << "JSON 序列化失败!" << std::endl;return false;}// 4. 将 stringstream 内容转为字符串*str = ss.str();return true;}/*** @brief 将字符串反序列化为 Json::Value 对象* @param str 输入的 JSON 格式字符串* @param root 输出的解析后的 JSON 数据结构* @return true 解析成功,false 解析失败*/static bool Unserialize(const std::string &str, Json::Value *root) {// 1. 创建 JSON 字符读取器构建器Json::CharReaderBuilder crb;// 2. 通过构建器生成 CharReader 对象(unique_ptr 自动管理内存)std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err;  // 存储解析错误信息// 3. 解析字符串// 参数说明:// - str.c_str():字符串起始地址// - str.c_str() + str.size():字符串结束地址// - root:输出解析后的 JSON 对象// - &err:错误信息输出bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);if (!ret) {std::cout << "JSON 解析错误: " << err << std::endl;return false;}return true;}};
};
#endif

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

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

相关文章

文号验证-同时对两个输入框验证

文号验证-同时对两个输入框验证 效果&#xff1a; 一、如果有多个文号&#xff1a; <div v-for"(item, index) in approvalForm.productApprovalTypeEvents" :key"index"> <el-form-itemlabel"文号":prop"productApprovalTypeEv…

高翔视觉slam中常见的OpenCV和Eigen的几种数据类型的内存布局及分配方式详解

vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d>> 内存布局及分配方式详解 1. 内存对齐的必要性 Eigen 的固定大小类型(如 Eigen::Vector2d、Eigen::Matrix4d 等)需要 16 字节内存对齐,以支持 SIMD 指令(如 SSE/AVX)的并行计算。若未对…

5G育种技术之植物性状订制

行业展望 我国农作物种业市场规模逐年增长&#xff0c;其中以粮食作物种子市场规模较大。目前我国育种产业发展仍处于初级阶段&#xff0c;存在龙头企业市场占有率和行业集中度不高、企业育种技术和水平落后于发达国家、种企研发投入不足等问题。虽然基因编辑技术的出现有望改…

用户隐私与社交媒体:评估Facebook的保护成效

在这个数字化时代&#xff0c;社交媒体平台&#xff0c;尤其是Facebook&#xff0c;已经成为我们生活中不可或缺的一部分。然而&#xff0c;随着用户隐私问题日益受到关注&#xff0c;社交媒体平台如何处理和保护用户数据成为了公众讨论的焦点。本文将探讨Facebook在用户隐私保…

python实现基于Windows系统计算器程序

Python实现Windows系统计算器程序&#xff08;含高级功能&#xff09; 下面我将介绍如何使用Python创建一个功能完整的Windows风格计算器程序&#xff0c;包含基本运算和高级数学功能。 1. 使用Tkinter实现基础计算器 import tkinter as tk from tkinter import ttk import …

Vue 3 响应式 API 详解与实战案例

Vue 3 引入了全新的响应式系统&#xff0c;主要通过 ref 和 reactive 这两个 API 来实现。下面我将通过具体代码示例详细讲解它们的用法和区别。 1. ref - 基础响应式 API ref 用于创建响应式的基本类型数据&#xff08;如字符串、数字、布尔值&#xff09;&#xff0c;也可以…

软件第三方测试:关键部分、意义、流程及方法全解析?

软件第三方测试是保障软件质量的关键部分&#xff0c;它由专业的机构来开展&#xff0c;这个机构不隶属于开发方和使用方&#xff0c;能以客观公正的视角找出软件问题。 测试意义 软件第三方测试意义重大&#xff0c;它依靠专业技术&#xff0c;依照严格流程&#xff0c;对软…

WPF TextBlock控件性能优化指南

WPF TextBlock控件性能优化指南 1. 引言 TextBlock作为WPF中最基础且使用最广泛的文本显示控件&#xff0c;其性能优化对整个应用程序的响应速度和资源占用有着重要影响。尽管TextBlock是一个轻量级控件&#xff0c;但在大型应用或需要显示大量文本的场景中&#xff0c;不恰当…

【Linux】关于虚拟机

一些在Linux驱动开发中使用虚拟机的经验。 部分图片和经验来源于网络&#xff0c;若有侵权麻烦联系我删除&#xff0c;主要是做笔记的时候忘记写来源了&#xff0c;做完笔记很久才写博客。 专栏目录&#xff1a;记录自己的嵌入式学习之路-CSDN博客 目录 1 VirtualBox使用技…

AimRT从入门到精通 - 04RPC客户端和服务器

一、ROS中的service通信机制 服务通信也是ROS中一种极其常用的通信模式&#xff0c;服务通信是基于请求响应模式的&#xff0c;是一种应答机制。也即&#xff1a;一个节点A向另一个节点B发送请求&#xff0c;B接收处理请求并产生响应结果返回给A。比如如下场景&#xff1a; 机器…

普通IT的股票交易成长史--20250502 突破(1)

声明&#xff1a;本文章的内容只是自己学习的总结&#xff0c;不构成投资建议。文中观点基本来自yt站方方土priceaction&#xff0c;综合自己的观点得出。感谢他们的无私分享。 送给自己的话&#xff1a; 仓位就是生命&#xff0c;绝对不能满仓&#xff01;&#xff01;&#…

《操作系统真象还原》调试总结篇

文章目录 前言第11章调试我们操作系统目前的内存管理现状 前言 上一章结尾调试还没有完成&#xff0c;本章开始前需要先完成上一章代码的调试。 总的来说&#xff0c;我们的操作系统目前有三大块内容&#xff1a;线程-进程内容、内存管理内容、中断内容。当然这三部分肯定不可…

【Machine Learning Q and AI 读书笔记】- 01 嵌入、潜空间和表征

Machine Learning Q and AI 中文译名 大模型技术30讲&#xff0c;主要总结了大模型相关的技术要点&#xff0c;结合学术和工程化&#xff0c;对LLM从业者来说&#xff0c;是一份非常好的学习实践技术地图. 本文是Machine Learning Q and AI 读书笔记的第1篇&#xff0c;对应原…

Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理

Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理 目录 Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理 一、简单介绍 二、Flutter 和 Android 原生之间的数据…

outlook for mac本地邮件存放在哪儿?

尽管 PST 格式通常与 Microsoft Outlook 联系在一起&#xff0c;但认为它也在 Mac OS 上存储邮箱数据是一种误解。实际上&#xff0c;Outlook for Mac 不会将邮件存储为 PST 文件。无法在 Outlook for Mac 中找到 PST 文件位置&#xff0c;因为它不使用 PST 文件来存储邮箱数据…

数字时代,如何为个人信息与隐私筑牢安全防线?

首席数据官高鹏律师团队编著 在当今数字化时代&#xff0c;个人信息和隐私保护至关重要。我们在享受数字生活带来的便利时&#xff0c;也面临着个人信息泄露、隐私被侵犯的风险。下面将从先进技术和法律途径两个方面&#xff0c;探讨如何严格保护个人信息和隐私。 一、先进技…

MongoDB的图形化工具robo3t,navicat

MongoDB 常用的两个图形化工具 —— Robo 3T 和 Navicat 的详细介绍、区别和基本使用方法&#xff1a; &#x1f9f0; 一、Robo 3T&#xff08;原 Robomongo&#xff09; &#x1f4cc; 简介 Robo 3T 是一款专注于 MongoDB 的轻量级可视化客户端。由原 Robomongo 团队开发&am…

Qt QWebEngine应用和网页的交互

一、QWebEngine简介 1、Qt WebEngine模块提供了一个Web浏览器引擎&#xff0c;可以轻松地将万维网上的内容嵌入到没有本机Web引擎的平台上的Qt应用程序中。 2、Qt WebEngine提供了用于渲染HTML&#xff0c;XHTML和SVG文档的C 类和QML类型&#xff0c;它们使用级联样式表&#…

d202552-sql

一、184. 部门工资最高的员工 - 力扣&#xff08;LeetCode&#xff09; 要找到每个部门工资最高的 使用窗口函数 加排序函数 排序函数用rank dense_rank都行 把最高相同的找出来就行 select *, dense_rank() over(partition by departmentId order by Salary desc) as rank …

AntSK:基于大模型的一体化AI知识库解决方案深度解析

随着大模型&#xff08;如GPT、LLM&#xff09;技术的飞速发展&#xff0c;企业对智能知识管理和专属AI助手的需求日益增长。AntSK 正是在这一背景下诞生的企业级AI一体机解决方案。本文将从技术架构、核心功能、创新点和应用场景等方面&#xff0c;深入解析 AntSK 如何助力企业…