云备份项目(代码片段)

李憨憨_ 李憨憨_     2022-10-24     532

关键词:

云备份项目


文章目录


云备份的认识

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

项目实现目标

服务端程序:部署在Linux服务器上
  实现针对客户端请求的业务处理:文件的上传备份,以及客户端浏览器的查看以及下载功能。并且具有热点管理功能,将非热点文件压缩存储节省磁盘空间
客户端程序:部署在Windows客户机上
  实现针对客户端主机上指定的文件夹中的文件,自动进行检测判断是否需要备份,需要则上传到服务器备份

模块划分


服务端:
网络通信模块:实现与客户端进行网络通信,并进行http协议数据解析
业务处理模块:明确客户端请求,并且进行对应的业务处理(上传文件,备份信息获取,文件下载)
数据管理模块:进行统一数据管理
热点管理模块:对服务器上备份的文件进行热点管理,将非热点文件进行压缩存储
  在我们的服务端,首先有一个网络通信模块,这个模块可以实现与任意客户端,其中我们有两个客户端,一个是浏览器,一个是我们自己写的备份客户端,其中我们的备份客户端专门向我们的网络通信模块,也就是我们的服务器里边发送一个文件上传请求而浏览器给服务器发送的请求是备份信息查看请求以及文件下载请求。
  在服务端接收到网络通信数据之后,在服务器里边有一个非常重要的模块:业务处理模块,它的功能就是,针对网络通信模块将数据接收上来,业务处理模块进行一个数据的分析,分析这个数据是一个什么样的请求,并且针对这个请求进行对应的一个业务处理。
  在服务端还有一个模块,叫做数据管理模块,该模块是专门进行数据管理的网络通信模块拿到数据之后进行解析,解析完毕之后业务处理模块进行业务处理,业务处理的过程中就会涉及到对数据的访问,不能直接访问数据,而是通过数据管理模块获取到数据,对数据进行操作,不管是管理,存储,还是获取都由数据管理模块统一进行,只需要把请求发送过来就行了。
  在服务端还有一个与业务处理模块并行运行是热点管理模块(服务器后台功能),专门检测服务器上边哪个文件的热度降低了,变成非热点文件了,把它压缩起来,如果业务处理模块有人要下载这个文件,先把文件解压缩之后再进行响应,毕竟它是一个非热点。
客户端:(备份客户端)
目录检测模块:遍历客户端主机上的指定文件夹,获取文件夹下所有的文件信息
数据管理模块:管理客户端所备份的所有文件信息
    (判断一个文件是否需要备份:1. 历史备份信息中不存在,2. 历史备份信息存在但是不一致)
网络通信模块:搭建网络通信客户端,将需要备份的文件上传备份到服务器

第三方库认识

JSON认识

json 是一种数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据。
例如:小明同学的学生信息

char name = "小明";
int age = 18;
float score[3] = 88.5, 99, 58;
当需要进行网络数据传输或者持久化存储的时候:需要按照指定的数据格式组织,这样才能用的时候解析出来
则json这种数据交换格式是将这多种数据对象组织成为一个字符串:
[
   
        "姓名" : "小明",
        "年龄" : 18,
        "成绩" : [88.5, 99, 58]
   ,
   
        "姓名" : "小黑",
        "年龄" : 18,
        "成绩" : [88.5, 99, 58]
   
]

json 数据类型:对象,数组,字符串,数字
对象:使用花括号 括起来的表示一个对象。
数组:使用中括号 [] 括起来的表示一个数组。
字符串:使用常规双引号 “” 括起来的表示一个字符串
数字:包括整形和浮点型,直接使用。
以键值对组成
说白了就是把多个数据格式化为一个指定格式的字符串
jsoncpp库:就是提供了一系列接口用于实现JSON格式的序列化和反序列化功能的。

//Json数据对象类
class Json::Value
    Value &operator=(const Value &other); //Value重载了[]和=,因此所有的赋值和获取数据都可以通过
    Value& operator[](const std::string& key);//简单的方式完成 val["姓名"] = "小明";
    Value& operator[](const char* key);
    Value removeMember(const char* key);//移除元素
    const Value& operator[](ArrayIndex index) const; //val["成绩"][0]
    Value& append(const Value& value);//添加数组元素val["成绩"].append(88); 
    ArrayIndex size() const;//获取数组元素个数 val["成绩"].size();
    std::string asString() const;//转string string name = val["name"].asString();
    const char* asCString() const;//转char*   char *name = val["name"].asCString();
    Int asInt() const;//转int int age = val["age"].asInt();
    float asFloat() const;//转float
    bool asBool() const;//转 bool
;

Json::Value类:jsoncpp库与外界进行数据交互的中间数据类
如果要将多个和数据对象进行序列化,则需要先实例化一个Json::Value对象,将数据加入其中
Json::Write类:jsoncpp库的一个序列化类
  成员接口:write()接口就是用于将Json::Value对象中的所有数据组织序列化成为一个字符串。
Json::Reader类:jsoncpp库的一个反序列类
  成员接口:parse()接口就是用于将一个json格式字符串,解析各个数据到Json::Value对象中
例:
这是一个序列化

#include <iostream>
#include <sstream>
#include <string>
#include <jsoncpp/json/json.h>
using namespace std;

void Serialize() 
	const char *name = "小明";
    int age = 18;
    float score[] = 77.5, 88, 99;
    //进行序列化
    //1.定义一个Json::Value对象,将数据填充进去
    Json::Value val;
    val["姓名"] = name;
    val["年龄"] = age;
    val["成绩"].append(score[0]);
    val["成绩"].append(score[1]);
    val["成绩"].append(score[2]);
    //2.使用StreamWriter对象进行序列化
    Json::StreamWriterBuilder swb;
    Json::StreamWriter *sw = swb.newStreamWriter();//new一个StreamWriter对象
    stringstream ss;
    sw->write(val, &os);//实现序列化
    cout << ss.str() << endl;
    delete sw;


int main()

	Serialize();
	return 0;


接下来再看一个反序列化

void UnSerialize(string &str) 
   Json::CharReaderBuilder crb;
   Json::CharReader *cr = crb.newCharReader();
   Json::Value val;
   string err;
   bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), &val, &err);
   if (ret == false) 
     cout << "UnSerialize failed:" << err << endl;
     delete cr;
     return ;
   
   cout << val["姓名"].asString() << endl;
   cout << val["性别"].asString() << endl;
   cout << val["年龄"].asString() << endl;
   int sz = val["成绩"].size();
   for (int i = 0; i < sz; ++i)                                                                    
     cout << val["成绩"][i].asFloat() << endl;
   
   delete cr;
   return;
 

bundle文件压缩库

BundleBundle 是一个嵌入式压缩库,支持23种压缩算法和2种存档格式。使用的时候只需要加入两个文件bundle.h 和 bundle.cpp 即可。
我们先创建出一个100M大小的文件

dd if=/dev/zero of=./hello.txt bs=100M count=1

 #include <iostream>                                                                                
 #include <fstream>
 #include <string>
 #include "bundle.h"
 using namespace std;
 
 //读取文件所有数据
 bool Read(const string &name, string *body) 
   ifstream ifs;
   ifs.open(name, ios::binary);//以二进制方式打开文件
   if (ifs.is_open() == false) 
     cout << "open failed!\\n";
     return false;
   
   ifs.seekg(0, ios::end);//fseek(fp, 0, SEEK_END);
   size_t fsize = ifs.tellg();//获取当前位置相对于文件起始位置的偏移量
   ifs.seekg(0, ios::beg);//fseek(fp, 0, SEEK_SET);
 
   body->resize(fsize);
   ifs.read(&(*body)[0], fsize);//string::c_str() 返回值 const char*
   if (ifs.good() == false) 
     cout << "read file failed!\\n";
     ifs.close();
     return false;
   
   ifs.close();
  return true; 
 
 
 //向文件写入数据
 bool Write(const string &name, const string &body) 
   ofstream ofs;
   ofs.open(name, ios::binary);//以二进制方式打开文件
   if (ofs.is_open() == false) 
     cout << "open failed!\\n";
     return false;
   
   ofs.write(body.c_str(), body.size());
   if (ofs.good() == false)                                                                        
     cout << "read file failed!\\n";
     ofs.close();
     return false;
   
   ofs.close();
   return true;
 
 
 void Compress(const string &filename, const string &packname) 
 string body;
   Read(filename, &body);//从filename文件中读取数据到body中
   string packed = bundle::pack(bundle::LZIP, body);//对body中的数据进行lzip格式压缩,返回压缩后数据
   Write(packname, packed);//将压缩后的数据写入到指定的压缩包中
 
 
 void UnCompress(const string &filename, const string &packname) 
   string packed;
   Read(packname, &packed);//从压缩包中读取压缩的数据
   string body = bundle::unpack(packed);//对压缩的数据进行解压缩
   Write(filename, body);//将解压缩胡的数据写入到新文件中
 
                                                                                                    
 int main()
 
   Compress("./hello.txt", "./hello.zip");
   UnCompress("./hi.txt", "./hello.zip");
   return 0;
 


要验证两个文件内容是否一致,那就计算两个文件的MD5值,进行对比是否一致
  MD5:是一种散列算法,会根据数据进行大量运算,最终得到一个最终结果(字符串),两个文件只要文件内容稍有差异,则最终得到的MD5值都是完全不一样的

很明显我们可以看出,这两个文件是一致的

httplib库

httplib 库,一个 C++11 单文件头的跨平台 HTTP/HTTPS 库。安装起来非常容易。只需包含 httplib.h 在你的代码中即可。
httplib 库实际上是用于搭建一个简单的 http 服务器或者客户端的库,这种第三方网络库,可以让我们免去搭建服务器或客户端的时间,把更多的精力投入到具体的业务处理中,提高开发效率。
http协议是一个应用层协议,在传输层基于tcp实现传输—因此http协议本质上是应用层的数据格式
http协议格式:
  请求:
    请求首行:请求方法URL协议版本\\r\\n
    头部字段:key:val\\r\\n的键值对
    空行:\\r\\n-用于间隔头部与正文
    正文:提交给服务器的数据
  响应:
    响应首行:协议版本 响应状态码 状态码描述\\r\\n
    头部字段:key: val\\r\\n的键值对
    空行:\\r\\n-用于间接头部与正文
    正文:响应给客户端的数据
在httplib库中,有两个数据结构,用于存放请求与响应信息:struct Request & struct Response





线程池中的线程获取连接进行处理:
  1.接收请求数据,并进行解析,将解析得到的请求数据放到了一个Request结构体变量req中;
  2.根据请求信息(请求方法&资源路径),到映射表中查找有没有对应的处理函数,如果没有则返回404;
  3.如果有对应映射信息-调用业务处理函数,将解析得到的req传入接口中,并且传入一个空的Response结构体变量rsp;
  4.处理函数中会根据req中的请求信息进行对应的业务处理,并且在处理过程中想rsp结构体中添加响应信息;
  5.等到处理函数执行完毕,则httplib得到了一个填充了响应信息的Response变量rsp;
  6.根据rsp中的信息,组织一个http格式的响应,发送给客户端;
  7.如果是短连接则关闭连接处理下一个,长连接则等待请求,超时则关闭处理下一个;

先看一个简单的文件上传的界面

<!--html是一个超文本标签语言,一个标签就可以理解是一个元素,一个控件-->
<html>
    <body>
        <!--form是一个表单域控件,当点击表单域提交按钮时,会将表单域中所有控件的数据进行组织提交-->
        <!--action是本次请求的资源路径;method是请求的方法;enctype是编码类型,是数据的组织格式-->
        <form action="http://192.168.122.000:9090/upload" method="post" enctype="multipart/form-data">
            <div>
                <input type="file" name="file">    <!--这是一个文件上传的选择框-->
            </div>    
            <div>
                <input type="submit" name="submit" value="上传">   <!--这是一个submit提交按钮-->
            </div>  
        </form>
    </body>
</html>


其数据组织格式是这样的

POST /upload HTTP/1.1
HOST: 192.168.122.130:9090
Connection: keep-alive
Content-Length: xxxxx
Content-Type: multipart/form-data; boundary=--xxxxxxxxxxxxxxxxxxxx

----xxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Disposition: form-data; name='file' filename=''
选中的那个文件的文件数据
----xxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Disposition: form-data; name='submit' 

上传(上传按钮的value值)
----xxxxxxxxxxxxxxxxxxxxxxxxxx

httplib库搭建服务器

有了这么一个界面之后,我们就可以搭建http服务器了

 #include "httplib.h"                                                                               
 using namespace std;
 
 void Numbers(const httplib::Request &req, httplib::Response &rsp)
 
   //这就是业务处理函数
   rsp.body = req.path;
   rsp.body += "-------------";
   rsp.body += req.matches[1];
   rsp.status = 200;
   rsp.set_header("Content-Type", "text/plain");
 
 
 void Upload(const httplib::Request &req, httplib::Response &rsp)
 
   //req.files MultipartFormDataMap   v--MultipartFormDataname, filename, content, content_type
   for (auto &v : req.files) 
     cout << v.second.name << endl;   //区域字段标识名
     cout << v.second.filename << endl;//如果是文件上传则保存文件名称
     cout << v.second.content << endl;//区域正文数据,如果是文件上传则是文件内容数据
   
 
 
 int main()
 
   //实例化server对象
   httplib::Server server;
   //添加映射关系--告诉服务器什么请求用哪个函数处理
   //因为数字没法确定固定数据,因此实际上人家用的是正则表达式--匹配符合制定规则的数据
   //正则表达式中 \\d 表示一个数字字符;+表示匹配前边的字符一次或多次;()表示单独捕捉括号内规则的数据
   server.Get("/numbers/(\\\\d+)", Numbers);
   server.Post("/upload", Upload);
 
   //0.0.0.0表示服务器任意地址;虚拟机需要关闭防火墙;云服务器需要设置安全组开通端口
   server.listen("0.0.0.0", 9090);
   return 0;
  

我们可以把action中的资源路径改成我们自己的虚拟机或者云服务器地址,然后当我们点击选择,选择我们刚写的HTML文件,然后点击上传,就可以看到一下结果

httplib库搭建客户端

 #include "httplib.h"                                                                               
 using namespace std;
 
 int main()
 
   httplib::Client client("192.168.19.xxx", 9090);
 
   //Result Get(const char *path, const Headers &headers);
   httplib::Headers header = 
     "connection", "close"
   ;
   //Response *res
   auto res = client.Get("/numbers/5678", header);
   if (res && res->status == 200) 
     cout << res->body << endl;
   
 
   //Result Post(const char *path, const MultiparFormDataItems &items);
   httplib::MultipartFormDataItems items;
   httplib::MultipartFormData item;
   item.name = "file";
   item.filename = "hi.txt";
   item.content = "hello nihao";
   item.content_type = "application/octet-stream";
   items.push_back(item);
 
   res = client.Post("/upload", items);
   return 0;
 


项目实现

云备份服务端实现

网络通信模块:通过httplib搭建http客户端与服务器实现网络通信
业务处理模块:针对客户端请求进行处理(上传,下载,展示界面)
数据管理模块:统一数据管理
热点管理模块:找出备份文件中的非热点文件,进行压缩存储

一个文件如果是非热点文件,我们会进行压缩存储,但是不管是否压缩,当别人获取展示界面的时候,我们都需要给出上传的文件信息,并且当客户端要下载文件的时候,也能找到对应的压缩包,进行解压后,返回源文件数据(不能是压缩包)

数据管理模块

数据管理模块:统一数据管理
  到底要管理什么数据:原文件名,原文件大小,原文件时间属性,对应的压缩包名称,压缩标志
    上传的文件,最终是要能够在浏览器上进行查看并下载的,而浏览器界面上我们需要能够展示客户端曾经备份过的文件:原文件名,文件大小,文件备份时间
    而一个非热点文件,如果被压缩了,获取到的大小就是压缩包大小,时间是压缩包的时间,然而页面上应该展示的是原文件的各项属性,而不是压缩包的属性
    一个文件一旦压缩了,压缩包会被存放到压缩包路径下,原文件就会被删除
    一个压缩包被解压缩,就应该把解压后的文件放到备份路径,压缩包应该被删除
要管理的数据信息已经确定了,问题是到底如何管理?
  数据的管理分为两部分:
    一部分是内存中的数据管理:查询效率更高—undered_mep(hash)云备份项目(代码片段)

云备份项目文章目录云备份项目云备份的认识项目实现目标模块划分第三方库认识JSON认识bundle文件压缩库httplib库httplib库搭建服务器httplib库搭建客户端项目实现云备份服务端实现数据管理模块文件操作工具类Json操作工具类文件... 查看详情

使用云祺虚拟机备份软件备份citrixxenserver虚拟机(代码片段)

1、进入云祺虚拟机备份系统,选择【备份/恢复】——【虚拟机备份】,新建备份任务。2、在CitrixXenServer虚拟化类型下勾选要备份的虚拟机,选择的虚拟机即显示在【已选择的虚拟机】下面,滚动页面到底部,点击【下一步】。... 查看详情

备份数据上传腾讯云cos(代码片段)

一、方案背景由于服务器上数据量越来越大,备份的数据会占用硬盘空间(虽说云硬盘可随时扩容,但是存在风险),定时将备份上传到COS。不仅节省服务器硬盘空间,也大大提高备份数据的安全性、可靠性。二、操作步骤1、软件... 查看详情

使用云祺虚拟机备份软件备份h3ccas虚拟机(代码片段)

1、进入云祺虚拟机备份系统,选择【备份/恢复】——【虚拟机备份】,新建备份任务。2、在H3CCAS虚拟化类型下勾选要备份的虚拟机,选择的虚拟机即显示在【已选择的虚拟机】下面,滚动页面到底部,点击【下一步】。3、备... 查看详情

备份本地库到远端ucloud云存储(代码片段)

备份本地mysql数据到Ucloud存储,支持STANDARD,IA,ARCHIVE标准存储,低频存储(IA)或者冷存储(ARCHIVE)这3中存储类型#注意如果,欲使用低频存储(IA)或者冷存储(ARCHIVE),请在命令参数storageclass中指定,支持三种值:STANDARD,IA,ARCHIVE#注意如果... 查看详情

openstack云主机跨项目迁移(代码片段)

...台修改网络类型为共享网络openstacknetworksetxxxxxxx--share2、备份数据库dockerexec-it-urootmariadbmysqldump-uroot-pxxxxxxxxxxxnovainstances>nova_instances.sqldockerexec-it-urootmariadbmysqldump-uroot-pxxxxxxxxxxxnovainstance_info_caches>nova_instance_info_caches.sqldockerexec-i... 查看详情

使用云祺虚拟机备份软件备份sanforhci虚拟机(代码片段)

1、进入云祺虚拟机备份系统,选择【备份/恢复】——【虚拟机备份】,新建备份任务。2、在SANFORHCI虚拟化类型下勾选要备份的虚拟机,选择的虚拟机即显示在【已选择的虚拟机】下面,滚动页面到底部,点击【下一步】。3、... 查看详情

项目云备份项目简介

项目简介:搭建云备份服务器客户端,实现客户端针对指定目录文件自动备份到云端,服务器端会对上传的文件热点判断,非热点压缩存储到磁盘。通过浏览器可以查看与下载(支持断点续传)。概要设计... 查看详情

项目云备份项目简介

项目简介:搭建云备份服务器客户端,实现客户端针对指定目录文件自动备份到云端,服务器端会对上传的文件热点判断,非热点压缩存储到磁盘。通过浏览器可以查看与下载(支持断点续传)。概要设计... 查看详情

项目云备份项目简介

项目简介:搭建云备份服务器客户端,实现客户端针对指定目录文件自动备份到云端,服务器端会对上传的文件热点判断,非热点压缩存储到磁盘。通过浏览器可以查看与下载(支持断点续传)。概要设计... 查看详情

mysql自动备份并提交到码云git仓库–任我乐(代码片段)

...对于个人开发者似乎也是一笔不小的费用。在创建自动将备份上传到码云仓库前,建议您先看下以下文章:《如何通过Git将本地项目推送到码云或GitHub》对于不太了解的朋友来说,此文章非常有用,否则在进行以下步骤时会出现... 查看详情

mysql自动备份并提交到码云git仓库–任我乐(代码片段)

...对于个人开发者似乎也是一笔不小的费用。在创建自动将备份上传到码云仓库前,建议您先看下以下文章:《如何通过Git将本地项目推送到码云或GitHub》对于不太了解的朋友来说,此文章非常有用,否则在进行以下步骤时会出现... 查看详情

公司实现全网备份项目:(rsync+定时任务)(代码片段)

...,现在领导要求你把数据在其他机器上做一个周期性定时备份。要求如下:每天晚上12点整在Web服务器A(web01)上打包备份网站程序目录并通过rsync命令推送到服务器B(backup)上备份保留(备份思路可以是先在本地按日期打包,然后再... 查看详情

使用宝塔面板如何自动备份数据库和网站代码(代码片段)

...章来自于某框架的知名苦工仙士可的提问,你是怎么定时备份数据库的?基础操作是:宝塔自带的定时任务当中就有备份数据库和网站代码啊  然后就直接选择了备份到服务器磁盘把备份文件放在服务器上也是不太保险,... 查看详情

mysql5.7.40备份到腾讯云cos+从cos恢复(代码片段)

1备份1.1安装coscli#wgethttps://github.com/tencentyun/coscli/releases/download/v0.12.0-beta/coscli-linux#mvcoscli-linux/usr/bin/coscli#chmod755/usr/bin/coscli#coscli--version如果github慢可以使用国内镜像:wgetht 查看详情

客快物流大数据项目:docker的迁移与备份(代码片段)

Docker的迁移与备份一、容器保存为镜像可以通过以下命令将容器保存为镜像dockercommitmynginxmynginx_image基于新创建的镜像创建容器dockerrun-di--name=mynginx2-p81:80mynginx_image访问81端口二、镜像备份可以通过以下命令将镜像保存为tar文... 查看详情

python项目所需依赖库的备份与还原(代码片段)

在项目根目录下执行下面命令pipfreeze>requirements.txt用于生成当前项目所依赖的所有依赖库清单pipdownload-rrequirements.txt-dpackages/下载所需依赖包到当前路径下的packages/目录下pipinstall--no-index--find-links=packages/-rrequirements.txt离线还... 查看详情

项目实践云备份概述

项目简介:搭载云备份服务器和客户端,实现客户端对主机上特定目录下的文件自动进行备份到云端服务器端对上传文件进行热点文件判断,对于非热点文件进行压缩存储节省磁盘空间支持通过浏览器进行查看和下载... 查看详情