【毕业项目】 云备份(一)

简介: 【毕业项目】 云备份

云备份项目认识

我们要做的这个项目一共要做到三点

  • 将本地计算机指定文件夹中的文件备份到服务器上
  • 可以通过浏览器查看并下载文件 支持断点续传功能
  • 服务器对于文件进行热点管理 对于非热点文件进行压缩以节省磁盘空间

b69dc50674c34a56a878b499f15cbbee.png

实现目标

该项目需要我们实现两端程序 包括运行在用户主机上的客户端 以及运行在Linux平台的服务器

通过这两端协作来完成上述的功能

服务端功能细分

  1. 支持客户端上传文件
  2. 支持客户端查看备份文件
  3. 支持客户端文件下载并且支持断点续传功能
  4. 热点文件管理

服务端模块化

服务端一共分为四个模块

  • 数据管理模块 主要功能是管理备份文件 以便于随时获取
  • 网络通信模块 主要功能是实现与客户端之间的网络通信
  • 业务处理模块 主要功能是文件上传 文件列表查看 下载
  • 热点管理 主要功能是对于长时间无访问的文件进行压缩

客户端功能细分

  1. 指定文件夹中的文件检测 (获取文件夹中有什么文件)
  2. 判断指定文件夹中的文件是否需要备份 (新增 已备份但是又需要修改 上次备份后又修改过 但是间隔了三秒没有修改 )
  1. 将需要备份的文件上传到服务器

客户端模块划分

客户端可以划分成三个模块

  • 数据管理模块 (管理备份的文件信息)
  • 文件检测模块 (监控指定的文件夹)
  • 文件备份模块 (上传需要的文件数据)

环境搭建

g++升级7.3版本

由于我们的项目需要用到更高版本的gcc 所以说我们需要先进行下环境搭建

我们可以通过

g++ --version

来进行g++版本的查看

接下来分别在命令行中输入下面四条指令

sudo yum install centos-release-scl-rh centos-release-scl
sudo yum install devtoolset-7-gcc devtoolset-7-gcc-c++
source /opt/rh/devtoolset-7/enable
echo "source /opt/rh/devtoolset-7/enable" >> ~/.bashrc

我们的g++版本就能迭代成最新的了

aa5037fd4c344132b072079961dc8d9f.png

安装jsoncpp库

我们可以通过下面两个指令来安装jsoncpp库

sudo yum install epel-release


sudo yum install jsoncpp-devel

可以通过下面的指令来查看jsoncpp库是否安装成功

ls /usr/include/jsoncpp/json/ -1

下载bundle数据压缩库

在下载bundle数据压缩库之前我们首先要安装git

git的安装方法可以参考我的这篇博客 git

之后使用下面这段指令就可以下载

git clone https://github.com/r-lyeh-archived/bundle.git

下载httplib库

使用下面这段指令就可以下载

git clone https://github.com/yhirose/cpp-httplib.git

第三方库认识

json认识

json 是一种数据交换格式 采用完全独立于编程语言的文本格式来存储和表示数据。

比如说我们要将小明同学的信息使用json存储

原来的信息如下

char name = "小明";
int age = 18;
float score[3] = {88.5, 99, 58};

转变为json存储之后如下

{
        "姓名" : "小明",
        "年龄" : 18,
        "成绩" : [88.5, 99, 58]
   }

json存储实际上是将原来的数据变成了一个字符串 (也就是说上面的代码实际上就是一个字符串)

json 数据类型:对象,数组,字符串,数字 介绍如下

  • 对象:使用花括号 {} 括起来的表示一个对象。
  • 字符串:使用常规双引号 “” 括起来的表示一个字符串
  • 数字:包括整形和浮点型,直接使用。
  • 数组:使用中括号 [] 括起来的表示一个数组。

也就是说 如果我们有多组同类型的数据 我们可以使用数组组织起来

代码表示如下

[
   {
        "姓名" : "小明",
        "年龄" : 18,
        "成绩" : [88.5, 99, 58]
   },
   {
        "姓名" : "小黑",
        "年龄" : 18,
        "成绩" : [88.5, 99, 58]
   }
]

jsoncpp – value类

jsoncpp 库用于实现 json 格式的序列化和反序列化,完成将多个数据对象组织成为 json 格式字符串,以及将 json格式字符串解析得到多个数据对象的功能

这其中最重要的是三个类 它们分别是value writer reader

value类的主要函数以及作用如下

//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
};


jsoncpp – writer类

//json序列化类,低版本用这个更简单
class JSON_API Writer {
  virtual std::string write(const Value& root) = 0;
}
class JSON_API FastWriter : public Writer {
  virtual std::string write(const Value& root);
  }
class JSON_API StyledWriter : public Writer {
  virtual std::string write(const Value& root);
}
//json序列化类,高版本推荐,如果用低版本的接口可能会有警告
class JSON_API StreamWriter {
    virtual int write(Value const& root, std::ostream* sout) = 0;
}
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
    virtual StreamWriter* newStreamWriter() const;
}

一般来说json序列化有两个类 一个低版本一个高版本

它们之间的区别在于 低版本的类直接返回一个字符串 而高版本的类返回一个文件描述符

jsoncpp – reader类

//json反序列化类,低版本用起来更简单
class JSON_API Reader {
 bool parse(const std::string& document, Value& root, bool collectComments = true);
}
//json反序列化类,高版本更推荐
class JSON_API CharReader {
    virtual bool parse(char const* beginDoc, char const* endDoc, 
                       Value* root, std::string* errs) = 0;
}
class JSON_API CharReaderBuilder : public CharReader::Factory {
    virtual CharReader* newCharReader() const;
}


reader类也分为低版本和高版本

其中低版本的reader类需要我们传入字符串 value对象 还有一个bool值

高版本的reader类需要我们传入字符串的起始位置 字符串的终止位置 value对象 还有一个string对象

jsoncpp – 实现序列化

比如说我们目前拥有以下的数据

   {
        "name" : "xiaoming",
        "age" : 18,
        "score" : [88.5, 99, 58]
   }

现在我们要将这些输出实现序列化的话 我们要分为以下两步走

  1. 将所有的数据存放在json::Value中
  2. 使用json::StreamWriter进行序列化

将所有的数据存放在json::Value中

代码表示如下

  const char* name = "xiaoming";    
  int age = 18;    
  float score[] = {77.5 , 88 , 93.6};    
  Json::Value root;    
  root["name"] = name;    
  root["age"] = age;    
  root["score"].append(score[0]);                                                                                     
  root["score"].append(score[1]);    
  root["score"].append(score[2]);    
  Json::StreamWriterBuilder swb;    
  std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());    
  std::stringstream ss;    
  sw->write(root , &ss); 

序列化后结果如下

image.png

jsoncpp – 实现反序列化

比如说我们目前拥有以下的jason字符串类格式类型的数据

  string str = R"({"name":"xiaohei" , "age":19 , "score":[58.5 , 66 , 35,5]})";    

这里介绍下 C++11中 R()语法

使用该语法之后括号内将为原生字符串 不受任何特殊字符的影响

举个例子 \n本来应该是换行符 可如果在()内的话 它就是两个字符组成的字符串“\n“

反序列化的本质其实就是将json格式的字符串转化为一个value对象

所以说实现反序列化也是分两步走

  1. 定义一个value对象
  2. 使用json::CharReader进行反序列化

代码表示如下

  // deserialize     
  string str = R"({"name":"xiaohei" , "age":19 , "score":[58.5 , 66 , 35,5]})";    
  Json::Value root; // instantiate a value objict                                                                     
  Json::CharReaderBuilder crb;  // subclass    
  std::unique_ptr<Json::CharReader> cr(crb.newCharReader());                       
  string err;                                         
  bool set = cr->parse(str.c_str() , str.c_str()+str.size() , &root , &err);    
  if (set == false)    
  {                                                                             
    std::cerr << "json parse error!" << std::endl;    
  }                    
  std::cout << root["name"].asString() << std::endl;    
  std::cout << root["age"].asString() << std::endl;    

bundle库认识

Bundle 是一个嵌入式压缩库 支持23种压缩算法和两种存档格式 使用的之后只需要加入两个头文件 bundle.cppbundle.h 即可

需要注意的是 这里的嵌入式和我们所熟知的嵌入式工程不同 它的意思是bundle库可以不需要编译 直接在我们的代码种使用

下面是bundle库的一些方法 大家简单浏览下即可

namespace bundle
{
  // low level API (raw pointers)
  bool is_packed( *ptr, len );
  bool is_unpacked( *ptr, len );
  unsigned type_of( *ptr, len );
  size_t len( *ptr, len );
  size_t zlen( *ptr, len );
  const void *zptr( *ptr, len );
  bool pack( unsigned Q, *in, len, *out, &zlen );
  bool unpack( unsigned Q, *in, len, *out, &zlen );
  // medium level API, templates (in-place)
  bool is_packed( T );
  bool is_unpacked( T );
  unsigned type_of( T );
  size_t len( T );
  size_t zlen( T );
  const void *zptr( T );
  bool unpack( T &, T );
  bool pack( unsigned Q, T &, T );
  // high level API, templates (copy)
  T pack( unsigned Q, T );
  T unpack( T );  

bundle库实现文件压缩

我们使用bundle库进行文件压缩实际上只需要用到一句代码

string pack(bundle::LZIP , string& body);

返回值说明:

它会返回一个string对象

参数说明:

  • 第一个参数是压缩的格式
  • 第二个参数是一个strting对象

实现bundle库文件压缩的代码如下

#include <iostream>
#include <fstream>
#include <string>
#include "bundle.h"
using std::cout;
using std::endl;
int main(int argc , char* argv[])
{
  if (argc < 3)
  {
    cout << "no enough arguements" << endl;
    return -1; 
  }
  std::string istreamstring = argv[1];
  std::string ostreamstring = argv[2];
  // open and copy the content of istreamstring     
  std::ifstream ifs;                                                                                                  
  ifs.open(istreamstring.c_str() , std::ios::binary);    
    // get the size of istreamstring     
  ifs.seekg(0 , std::ios::end);
  int size = ifs.tellg();    
  ifs.seekg(0 , std::ios::beg);  
      // get end 
  std::string body;
  body.resize(size);
  ifs.read(&body[0] , size); // &body[0] is a wonderful code  because if we use c_str we just get a const str but &body[0] can give us a str whihout const 
  std::string packed = bundle::pack(bundle::LZIP , body); // compress in lzip format 
  std::ofstream ofs;
  ofs.open(ostreamstring , std::ios::binary);
  ofs.write(&packed[0] , packed.size());
  ifs.close();
  ofs.close();
  return 0;
}

我们编译运行之后可以发现3e5e2a9c8b48475db04d6f612151881e.png

文件压缩后的大小确实比文件压缩前的大小要小不少

bundle库实现文件解压缩

我们要证明压缩后的文件确实是源文件的压缩的话 就需要解压之后对比两个文件的md5值即可

于是乎我们下面就来实现一个文件解压缩的代码

bundle库解压缩的代码如下

string unpack(string body);

返回值说明:

它会返回一个string对象

参数说明:

它的参数只有一个 是一个string对象 里面是被压缩的文件内容

实现bundle库文件解压缩的代码如下

#include <iostream>    
using std::cout;    
using std::endl;    
#include <string>    
using std::string;    
#include <fstream>    
#include "bundle.h"    
int main(int argc , char* argv[])    
{    
  if (argc < 3)    
  {    
    cout << "no enough arguements" << endl;    
    return -1;    
  }    
  string ifilestring = argv[1];    
  string ofilestring = argv[2];    
  // open and copy the file     
  std::ifstream ifs;                                                                                                  
  ifs.open(ifilestring , std::ios::binary);    
  ifs.seekg(0 , std::ios::end);    
  size_t size = ifs.tellg();    
  ifs.seekg(0 , std::ios::beg);    
  string body; 
  body.resize(size);
  ifs.read(&body[0] , size);
  // uncompress                                                                                                       
  string unpacked = bundle::unpack(body);
  // write to ofilestring 
  std::ofstream ofs;
  ofs.open(ofilestring , std::ios::binary);
  ofs.write(&unpacked[0] , unpacked.size());
  // close stream 
  ifs.close();
  ofs.close();
  return 0;
}

编译运行之后我们可以发现 MD5值和大小一模一样 所以说压缩和解压缩功能正常

24b603294e34467dab77f3bd88892397.png

httplib库认识

httplib 库 一个 C++11 单文件头的跨平台 HTTP/HTTPS 库 安装起来非常容易 只需包含 httplib.h 在你的代码中即可

httplib 库实际上是用于搭建一个简单的 http 服务器或者客户端的库 这种第三方网络库 可以让我们免去搭建服务器或客户端的时间 把更多的精力投入到具体的业务处理中 提高开发效率

博主自己其实也实现过一个简单的Http服务器 如果大家有兴趣可以参考博客

自主实现http服务器

http服务器代码

httplib 库搭建简单服务器

下面介绍的内容是如何使用httplib库来搭建一个简单的服务器

首先我们需要利用httplib里面的Sever类实例化一个对象

httplib::Server server; // instantiate a sever object 

之后调用这个对象的函数去针对请求的各种资源注册各种函数

 server.Get("/hi" , Hello); // register a function method for get request resource hi 
  void Hello(const httplib::Request& req , httplib::Response& res)    
  {                                                                                                                   
    res.body = "Hello";    
    res.status = 200;    
  }   

之后让这个对象去监听服务器的网卡

server.listen("0.0.0.0" , 8081);

最后让服务器运行起来之后我们使用浏览器访问特定资源便可以收到特定的回复

9dd5c540d21a44cc9d44663dae2e5158.png


httplib 库搭建简单客户端

首先我们需要利用httplib库里面的Client类实例化一个对象

httplib::Client client(SEVER_IP, SEVER_PORT);  // ip  port

上面的IP和端口均为服务器的

紧接着我们就可以使用这个对象去调用函数来得到一个指针

使用该指针可以获得我们想要的各种数据

  auto res = client.Get("/hi");    
  cout << res->status << endl;    
  cout << res->body << endl;   

而如果我们要请求的是post方法 我们则需要进行下面的操作

      // 2. send data to client
      httplib::Client client(SERVER_ADDR, SERVER_PORT); 
      httplib::MultipartFormData item;
      item.content = body;
      item.filename = fu.FileName();
      item.name = "file"; // mark
      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;
      }

我们需要定义一个item 并且将数据填充完毕之后添加到items中 zu欧维一个Post函数的参数上传

相关文章
|
存储 JSON 数据管理
【毕业项目】 云备份(二)
【毕业项目】 云备份(二)
70 0
|
存储 监控 数据管理
【毕业项目】 云备份(三)
【毕业项目】 云备份(三)
128 0
|
运维 关系型数据库 MySQL
企业运维训练营之数据库原理与实践—云数据库备份与恢复—备份恢复实战
企业运维训练营之数据库原理与实践—云数据库备份与恢复—备份恢复实战
129 0
|
SQL 运维 AliSQL
企业运维训练营之数据库原理与实践—云数据库备份与恢复—云上备份恢复能力与场景
企业运维训练营之数据库原理与实践—云数据库备份与恢复—云上备份恢复能力与场景
145 0
|
存储 Cloud Native 关系型数据库
【备考心得】教你如何顺利通过阿里云PolarDB开源人才培养考试
本次考试的经验与心得分享,含关键知识点、考点总结,助你顺利通过考试。
|
弹性计算 关系型数据库 MySQL
鹤酒全栈初入江湖,ECS服务器
鹤酒全栈初入江湖,ECS服务器
|
关系型数据库 MySQL Linux
上云第一课第一期部署MySQL数据库
主要讲述数据的部署以及使用
上云第一课第一期部署MySQL数据库
|
前端开发 Java 应用服务中间件
使用阿里云部署全栈项目
了解阿里云,学习部署项目
|
SQL 存储 安全
内附PPT下载 | 阿里云资深技术专家 陈长城:一站式数据管理DMS及最新解决方案解读
阿里云资深技术专家陈长城在“数聚云端·智驭未来”——阿里云数据库创新上云峰会上,做了一站式数据管理平台DMS以及解决方案的发布。议题包含企业数据管理当前的一些痛点、DMS一站式数据管理平台以及其核心技术、实时数仓解决方案以及相应的应用实践等。
1760 0
内附PPT下载 | 阿里云资深技术专家 陈长城:一站式数据管理DMS及最新解决方案解读
|
Web App开发 前端开发 JavaScript
致青春!一键上线你们专属的云上毕业纪念册
毕业不说再见,青春不散场!在云端,在一起!在问答https://developer.aliyun.com/ask/321737的留言区域晒出自己「线上环境」部署的毕业纪念册,在6月30号18点之前点赞数前10可以获得我们送出的毕业大礼包,阿里云的公仔盲盒一个以及10元的代金券一张,让你的青春永远在线!
致青春!一键上线你们专属的云上毕业纪念册