【项目实战】--云备份系统

1、云备份认识

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

2、实现目标

这个云备份项目需要我们实现两端程序,其中包括部署在用户机的客户端程序,上传需要备份的文件,以及运行在服务器上的服务端程序,实现备份文件的存储和管理,两端合作实现总体的自动云备份功能。

3、服务端程序负责功能

  • 针对客户端上传的文件进行备份存储
  • 能够对文件进行热点文件管理,对非热点文件进行压缩处理,节省磁盘空间
  • 支持客户端浏览器查看访问文件列表
  • 支持客户端浏览器下载文件,并且支持下载支持断点续传

4、服务端功能模块划分

  • 数据管理模块:管理备份的文件信息,以便随时获取
  • 网络通信模块:搭建网络通信服务器,实现与客户端的网络通信
  • 业务处理模块:文件上传请求,列表查看,文件下载请求(断点续传)
  • 热点管理模块:负责文件的热点判断,对长时间无访问文件进行压缩存储

5、客户端程序负责功能

  • 能够自动监测客户机指定文件夹中的文件,并判断是否需要备份
  • 将需要备份的文件逐个上传到服务器

6、客户端功能模块划分

  • 数据管理模块:负责客户端备份的文件信息管理,通过这些数据可以确定一个文件是否需要备份
  • 文件监测模块:遍历获取指定文件夹中所有文件路径名称,监控指定的文件夹
  • 网络通信模块:搭建网络通信客户端,实现将文件备份上传到服务器

7、环境搭建-gcc升级7.3版本

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

如下图所示就表示已经配置好了
在这里插入图片描述

8、环境搭建-安装jsoncpp库

sudo yum install epel-release
sudo yum install jsoncpp-devel

如下图所示即为安装成功
在这里插入图片描述

9、环境搭建-下载bundle数据压缩库

sudo yum install git
git clone https://github.com/r-lyeh-archived/bundle.git
在这里插入图片描述

10、环境搭建-下载httplib库

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

11、第三方库认识-json认识

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

char name = “张三”;
int age = 18;
float score[3] = {88.5,99,58}
char name = “张三”;
int age = 18;
float score[3] = {88.5,99,58}

则json这种数据交换格式是将多重数据对象组织成为一个字符串
[
{
 	"姓名":"张三",
	"年龄":18,
	"成绩" :[88.5,99,58]	
},
{
 	"姓名":"李四",
	"年龄":18,
	"成绩" :[88.5,99,58]	
}
]

json数据类型:对象、数据、字符串、数字
对象:使用花括号{}括起来的表示一个对象
数组:使用中括号[]括起来的表示一个数组
字符串:使用常规双引号""括起来的表示一个字符串
数字:包括整形喝浮点型,直接使用

12、第三方库认识–jsoncpp认识

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

这其中主要借助三个类以及其对应的少量成员函数完成

class Json::Value{
	Value &operator=(const Value &other);//Value重载了[]和=,因此所有的赋值和获取数据都可以通过
    Value &operator[](const std::string& key);//简单的方式完成val["name"] = "张三"
    Value &operator[](const char* key);
    Value removeMember(const char* key);//移除元素
    const Value& operator[](ArrayIndex index)const; //val["score"][0]
    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();//转float
    bool asBool()const;//转bool 
    Value& append(const Value& value);//添加数组元素 val["score"].append(88);
    ArrayIndex size()const;//获取数组元素个数
};


//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 StyleWriter:public Writer{
    virtual std::string write(const Value& root);
}
//JSON序列化类,高版本推荐,如果用低版本的借口可能会有警告
class JSON_API StreamWriter{
    virtual int writer(Value const& root,std::ostream* scout) = 0;
}
class JSON_API StreamWriterBuilder:public StreamWriter:Factory{
    virtual StreamWriter* newStgreamWriter() const;
}
//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* enDoc,
    Value* root,std::string* errs) = 0;
}
class JSON_API charReaderBuilder:public charReader:Factory{
    virtual charReader* newcharReader()const;
}

下面我们使用上面的接口来实现序列化编程以加深印象

运行结果

#include<iostream>
#include<sstream>
#include<string>
#include<memory>
#include<stdlib.h>
#include<jsoncpp/json/json.h>

int main()
{
    const char* name ="小明";
    int age = 18;
    float score[] = {70.1,80.2,90.3};

    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);
    std::cout<<ss.str()<<std::endl;

    return 0;
}

在这里插入图片描述
下面我们使用上面的接口来实现反序列化编程以加深印象

#include<iostream>
#include<string>
#include<memory>
#include<jsoncpp/json/json.h>


int main()
{
    std::string str = R"({"姓名":"李四","年龄":19,"成绩":[10.1,20.2,30.3]})";
    Json::Value root;
    Json::CharReaderBuilder crb;
    std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
    std::string err;
    bool ret = cr->parse(str.c_str(),str.c_str()+str.size(),&root,&err);
    if(ret == false)
    {
        std::cout<<"parse error:"<<err<<std::endl;
        return -1;
    }
    std::cout<<root["姓名"].asString()<<std::endl;
    std::cout<<root["年龄"].asInt()<<std::endl;
    int sz = root["成绩"].size();
    for(int i = 0;i<sz;i++)
    {
        std::cout<<root["成绩"][i]<<std::endl;
    }
    return 0;

}

运行结果:
在这里插入图片描述

13、第三方库认识-bundle文件压缩库认识

BundleBund 是一个嵌入式压缩库,支持23种压缩算法和2种存储格式。使用的时候只需要加入两个文件bundle.h和bundle.cpp即可

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 pack(T &,T)
    bool unpack(unsigned Q,*in,T &,T);

    //high level API,template (copy)
    T pack*(unsigned Q,T);
    T unpack(T);    
}

压缩文件

------------------comprise.cpp-----------------------
#include<iostream>
#include<string>
#include<fstream>
#include"bundle.h"

int main(int argc,char* argv[])
{
   std::cout<<"argv[1]是原始文件路径名称"<<std::endl;
   std::cout<<"argv[2]是压缩包名称"<<std::endl;

   if(argc<3) return -1;
   std::string ifilename = argv[1];
   std::string ofilename = argv[2];

   std::ifstream ifs;
   ifs.open(ifilename,std::ios::binary);//打开原始文件
   ifs.seekg(0,std::ios::end);//跳转读写位置到末尾
   size_t fsize = ifs.tellg();//获取末尾偏移量--文件长度
   ifs.seekg(0,std::ios::beg);//跳转到文件起始
   std::string body;
   body.resize(fsize);//调整body大小为文件大小
   ifs.read(&body[0],fsize);//读取文件所有数据到body找给你

   std::string packed = bundle::pack(bundle::LZIP,body);//以lzip格式压缩文件数据

   std::ofstream ofs;
   ofs.open(ofilename,std::ios::binary);//打开压缩包文件
   ofs.write(&packed[0],packed.size());//将压缩后的数据写入压缩包文件

   ifs.close();
   ofs.close();
   return 0;
}

编译运行压缩bundle.cpp文件:
在这里插入图片描述
解压缩文件

14、第三方库认识-bundle文件压缩库认识

-----------------------------uncomprise.cpp----------------------------------------
#include<iostream>
#include<fstream>
#include<string>
#include"bundle.h"


int main(int argc,char *argv[])
{
    if(argc<3)
    {
        std::cout<<"argv[1]是压缩包名称"<<std::endl;
        std::cout<<"argv[2]是解压后的文件民称"<<std::endl;
        return -1;
    }
    std::string ifilename = argv[1];//压缩包名
    std::string ofilename = argv[2];//解压缩后的文件名

    std::ifstream ifs;
    ifs.open(ifilename,std::ios::binary);
    ifs.seekg(0,std::ios::end);
    size_t fsize = ifs.tellg();
    ifs.seekg(0,std::ios::beg);
    std::string body;
    body.resize(fsize);
    ifs.read(&body[0],fsize);
    ifs.close();

    std::string unpacked = bundle::unpack(body);
    std::ofstream ofs;
    ofs.open(ofilename,std::ios::binary);
    ofs.write(&unpacked[0],unpacked.size());
    ofs.close();
    return 0;
}

编译运行解压缩bundle.cpp.lz文件:
在这里插入图片描述

15、第三方库认识-httplib库

httplib库,一个C++单文件头的跨平台HTTP/HTTPS库。安装起来非常容易。只需包含httplib.h在你的代码中即可
httplib库实际上是用于搭建一个简单的http服务器或者客户端的库,这种第三方网络库,可以让我们免去搭建服务器或客户端的时间,把更多的精力投入到具体的业务处理中,提高开发效率

namespace httplib
{
    struct MultipartFormData{
        std::string name;
        std::string content;
        std::string filename;
        std::string content_type;
    };
    using MultipartFormDataItems = std::vector<MultipartFormData>;
    struct Resquest{
        std::string method;//请求方法 GET\POST\HEAD....
        std::string path;//URL资源路径
        Headers headers;//map 头部字段
        std::string body;//请求正文
        //for server
        std::string version;//协议版本
        Params params;//map 查询字符串
        MultipartFormDataMap files;//客户端上传的文件信息
        Ranges ranges;//用于实现断点续传的请求文件区间

        bool has_header(const char* key)const;
        std::string get_header_value(const char* key,size_t id = 0)const;
        void set_header(const char* key)const;
        MultipartFormData get_file_value(const char* key)const; 
    };
    struct Response{
        std::string version;//版本协议
        int status = -1;//响应状态码
        std::string reason;
        Headers headers;//头部字段
        std::string body;//响应给客户端的正文
        std::string location;//重定向位置
        void set_header(const char*key,const char* val);//设置头部字段
        void set_content(const std::string &s,const char* content_type);//设置正文
    };
    class Server{
        using Handler = std::function<void(const Request &,Response &)>; //函数指针类型,定义了一个http请求处理回调函数格式

        //请求与处理函数映射表(请求路由数组),regex是一个正则表达式,用于匹配http请求资源路径;handler是请求处理函数指针
        using Handlers = std::vector<std::pair<std::regex,Handler>>;

        //线程池--用于处理请求,httplib收到一个新建连接,就把新的客户端抛入线程池中
        //1、接受请求。解析请求,达到Request结构体,也就是请求的数据
        //2、在handlers映射表中,根据请求信息查找处理函数,有就调用函数处理
        //3、当处理函数调用完毕,根据函数返回的Response结构体中的数据组织http相应发送给客户端
        std:function<TaskQueue* (void) new_task_queue;

        //针对某种请求方法的某个请求设定映射的处理函数
        Server &Get(const std::string &pattern,Handler handler);
        Server &Post(const std::string &pattern,Handler handler);
        Server &Put(const std::string &pattern,Handler handler);
        Server &Patch(const std::string &pattern,Handler handler);
        Server &Delete(const std::string &pattern,Handler handler);
        Server &Options(const std::string &pattern,Handler handler);

        //搭建并启动http服务器
       bool listen(const char* host,int port,int socket_flags = 0);
    }
    class Client{
        Client(const std::string &host,int port);//传入服务器IP地址和端口
        Result Get(const char* path,const Headers &headers);//向服务器发送Get请求
        Result Post(const char* path,const char* body,size_t content_length,const char* content_type);//向服务器发送Post请求
        Result Post(const char* path,const MultipartFormDataItems &items);//post请求提交多区域数据,常用于多文件
    }
}

httplib搭建简单服务器

#include"httplib.h"

void Hello(const httplib::Request &req,httplib::Response &rsp)
{
    rsp.set_content("hello world","text/plain");
    rsp.status = 200;
}
void Numbers(const httplib::Request &req,httplib::Response &rsp)
{
    auto num = req.matches[1];//0里面保存的是整体path,往后下标中保存的是捕捉的数据
    rsp.set_content(num,"text/plain");
    rsp.status = 200;
}
void Multipart(const httplib::Request &req,httplib::Response &rsp)
{
    auto ret = req.has_file("file");
    if(ret == false)
    {
        std::cout<<"not file uplaod\n";
        rsp.status = 400;
        return;
    }
    const auto &file = req.get_file_value("file");
    rsp.body.clear();
    rsp.body = file.filename;//文件名称
    rsp.body+="\n";
    rsp.body = file.content;//文件内容
    rsp.set_header("Content-Type","text/plain");
    rsp.status = 200;

    return;
}
int main()
{
    httplib::Server server;//实例化server类的对象用于搭建服务器

    server.Get("/hi",Hello);//注册一个针对/hi的Get请求的处理函数映射关系
    server.Get(R"(/numbers/(\d+))",Numbers);
    server.Post("/multipart",Multipart);
    server.listen("0.0.0.0",9090);

    return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

httplib搭建简单客户端

#include"httplib.h"

#define SERVER_IP "106.54.9.13"
#define SERVER_PORT 9090
int main()
{
    httplib::Client client(SERVER_IP,SERVER_PORT);//实例化client对象,用于搭建客户端

    httplib::MultipartFormData item;
    item.name = "file";
    item.filename = "hello.txt";
    item.content = "hello world!";//上传文件时,这里给的就是文件内容
    item.content_type = "text/plain";

    httplib::MultipartFormDataItems items;
    items.push_back(item);

    auto res = client.Post("/multipart",items);
    std::cout<<res->status<<std::endl;
    std::cout<<res->body<<std::endl;
    return 0;
}

运行结果:
在这里插入图片描述

16、服务端工具类实现–文件实用工具类设计

不管是客户端还是服务端,文件的传输备份都涉及到文件的读写,包括数据管理信息的持久化也是如此,因此首先设计封装文件操作类,这个类封装完毕之后,则在任意模块中对文件进行操作时都将变得简单化。

--------------util.hpp----------------------
class FileUtil
{
private:
    std::string _name;
public:
    FileUtil(const std::string &name);
    size_t FileSize();//获取文件大小
    time_t LastAccTime();//;//获取文件最后一次访问时间
    time_t LastModTime();//获取文件最后一次修改时间
    std::string FileName();//获取文件路径名中的文件名称 /a/b/.txt ->c.txt
    bool GetPosLen(std::string *content,size_t pos,size_t len);//获取文件指定位置指定长度的数据
    bool GetContent(std::string *content);//获取文件数据
    bool SetContent(const std::string &content);//向文件写入数据
    bool Compress(const std::string &packname);//压缩
    bool UnCompress(const std::string &filename);//解压缩
    bool Exists();//判断文件是否存在
    bool CreateDirctory();
    bool GetDirectory(std::vector<std::string> *array);//获取指定文件夹下所有文件路径名称
};

服务端工具类实现

#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>

namespace maria{
    class FileUtil{
    public:
        FileUtil(const std::string &filename)
        :_filename(filename)
        {}
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
                {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        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 error!\n";
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename,std::ios::binary);
            if(ifs.is_open()==false)
            {
                std::cout<<"open file failed while reading!\n";
                return false;
            }

            ifs.seekg(pos,std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0],len);
            if(ifs.good()==false)
            {
                std::cout<<"get file content failed\n";
            }
            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<<"open file failed while writting!\n";
                return false;
            }
            ofs.write(&body[0],body.size());
            if(ofs.good()==false)
            {
                std::cout<<"write file content failed!\n";
                return false;
            }
            ofs.close();
            return true;
        }
    private:
        std::string _filename;
    };
};


-----------------------cloud.cpp------------------------
#include"util.hpp"

void FileUtilTest1(const std::string &filename)
{
    maria::FileUtil fu(filename);
    std::cout<<"FileSize:"<<fu.FileSize()<<std::endl;
    std::cout<<"LastMTime:"<<fu.LastMTime()<<std::endl;
    std::cout<<"LastATime:"<<fu.LastATime()<<std::endl;
    std::cout<<"FileName:"<<fu.FileName()<<std::endl;
}
void FileUtilTest2(const std::string &filename)
{
    maria::FileUtil fu(filename);
    std::string body;
    fu.GetContent(&body);

    maria::FileUtil nfu("./hello.txt");
    nfu.SetContent(body);

}
int main(int argc,char* argv[])
{
    FileUtilTest1(argv[1]);
    FileUtilTest2(argv[1]);
    return 0;
}

运行结果:
在这里插入图片描述

------------------util.hpp-------------------------------------
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include"bundle.h"

namespace maria{
    class FileUtil{
    public:
        FileUtil(const std::string &filename)
        :_filename(filename)
        {}
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
                {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        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 error!\n";
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename,std::ios::binary);
            if(ifs.is_open()==false)
            {
                std::cout<<"open file failed while reading!\n";
                return false;
            }

            ifs.seekg(pos,std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0],len);
            if(ifs.good()==false)
            {
                std::cout<<"get file content failed\n";
            }
            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<<"open file failed while writting!\n";
                return false;
            }
            ofs.write(&body[0],body.size());
            if(ofs.good()==false)
            {
                std::cout<<"write file content failed!\n";
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const std::string &packname)
        {
            //1、获取源文件数据
            std::string body;
            if(this->GetContent(&body)==false)
            {
                std::cout<<"compress get file content failed!\n";
                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!\n";
                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!\n";
                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!\n";
                return false;
            }
            return true;
        }
    private:
        std::string _filename;
    };
};
-----------cloud.cpp--------------------------------
#include"util.hpp"
void FileUtilTest3(const std::string &filename)
{
    std::string packname = filename+".lz";
    maria::FileUtil fu(filename);
    fu.Compress(packname);
    maria::FileUtil pfu(packname);
    pfu.UnCompress("./hello.txt");
}
int main(int argc,char* argv[])
{
    // FileUtilTest1(argv[1]);
    // FileUtilTest2(argv[1]);
    FileUtilTest3(argv[1]);
    return 0;
}

运行结果:在这里插入图片描述

-----------------util.hpp---------------------------
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<experimental/filesystem>
#include"bundle.h"

namespace maria{
    namespace fs = std::experimental::filesystem;
    class FileUtil{
    public:
        FileUtil(const std::string &filename)
        :_filename(filename)
        {}
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
                {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        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 error!\n";
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename,std::ios::binary);
            if(ifs.is_open()==false)
            {
                std::cout<<"open file failed while reading!\n";
                return false;
            }

            ifs.seekg(pos,std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0],len);
            if(ifs.good()==false)
            {
                std::cout<<"get file content failed\n";
            }
            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<<"open file failed while writting!\n";
                return false;
            }
            ofs.write(&body[0],body.size());
            if(ofs.good()==false)
            {
                std::cout<<"write file content failed!\n";
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const std::string &packname)
        {
            //1、获取源文件数据
            std::string body;
            if(this->GetContent(&body)==false)
            {
                std::cout<<"compress get file content failed!\n";
                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!\n";
                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!\n";
                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!\n";
                return false;
            }
            return true;
        }
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool CreateDirectory()
        {
            if(this->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;
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
        std::string _filename;
    };
};

-----------------cloud.cpp---------------------------
#include"util.hpp"

// void FileUtilTest1(const std::string &filename)
// {
//     maria::FileUtil fu(filename);
//     std::cout<<"FileSize:"<<fu.FileSize()<<std::endl;
//     std::cout<<"LastMTime:"<<fu.LastMTime()<<std::endl;
//     std::cout<<"LastATime:"<<fu.LastATime()<<std::endl;
//     std::cout<<"FileName:"<<fu.FileName()<<std::endl;
// }
// void FileUtilTest2(const std::string &filename)
// {
//     maria::FileUtil fu(filename);
//     std::string body;
//     fu.GetContent(&body);

//     maria::FileUtil nfu("./hello.txt");
//     nfu.SetContent(body);

// }
// void FileUtilTest3(const std::string &filename)
// {
//     std::string packname = filename+".lz";
//     maria::FileUtil fu(filename);
//     fu.Compress(packname);
//     maria::FileUtil pfu(packname);
//     pfu.UnCompress("./hello.txt");
// }
void FileUtilTest4(const std::string &filename)
{
    maria::FileUtil fu(filename);
    fu.CreateDirectory();
    std::vector<std::string> array;
    fu.ScanDirectory(&array);
    for(auto &a : array)
    {
        std::cout<<a<<std::endl;
    }
}
int main(int argc,char* argv[])
{
    // FileUtilTest1(argv[1]);
    // FileUtilTest2(argv[1]);
    // FileUtilTest3(argv[1]);
    FileUtilTest4(argv[1]);
    return 0;
}

在这里插入图片描述

17、服务端工具类实现–Json实用工具类设计

----------------------------util.hpp--------------------------------------
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<experimental/filesystem>
#include<jsoncpp/json/json.h>
#include"bundle.h"

namespace maria{
    namespace fs = std::experimental::filesystem;
    class FileUtil{
    public:
        FileUtil(const std::string &filename)
        :_filename(filename)
        {}
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
                {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        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 error!\n";
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename,std::ios::binary);
            if(ifs.is_open()==false)
            {
                std::cout<<"open file failed while reading!\n";
                return false;
            }

            ifs.seekg(pos,std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0],len);
            if(ifs.good()==false)
            {
                std::cout<<"get file content failed\n";
            }
            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<<"open file failed while writting!\n";
                return false;
            }
            ofs.write(&body[0],body.size());
            if(ofs.good()==false)
            {
                std::cout<<"write file content failed!\n";
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const std::string &packname)
        {
            //1、获取源文件数据
            std::string body;
            if(this->GetContent(&body)==false)
            {
                std::cout<<"compress get file content failed!\n";
                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!\n";
                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!\n";
                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!\n";
                return false;
            }
            return true;
        }
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool CreateDirectory()
        {
            if(this->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;
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
        std::string _filename;
    };
    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root,std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            if(sw->write(root,&ss)!=0)
            {
                std::cout<<"json write failed!\n";
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str,Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(),str.c_str()+str.size(),root,&err);
            if(ret == false)
            {
                std::cout<<"parse error:"<<err<<std::endl;
                return false;
            }
            return true;
        }
    };
};

-------------------Makefile---------------------------
cloud:cloud.cpp util.hpp  bundle.cpp
	g++ -o $@ $^  -lpthread -lstdc++fs -ljsoncpp
.PHONY:clean
clean:
	rm -f cloud
---------------------cloud.cpp---------------------
#include"util.hpp"
void JsonUtilTest()
{
    const char* name = "张三";
    int age = 19;
    float score[]={10.1,20.2,30.3};
    Json::Value root;
    root["姓名"] = name;
    root["年龄"] = age;
    root["成绩"].append(score[0]);
    root["成绩"].append(score[1]);
    root["成绩"].append(score[2]);
    std::string json_str;
    maria::JsonUtil::Serialize(root,&json_str);
    std::cout<<json_str<<std::endl;
    Json::Value val;
    maria::JsonUtil::UnSerialize(json_str,&val);
    std::cout<<val["姓名"].asString()<<std::endl;
    std::cout<<val["年龄"].asInt()<<std::endl;
    for(int i = 0;i<val["成绩"].size();i++)
    {
        std::cout<<val["成绩"][i].asFloat()<<" ";
    }
    std::cout<<std::endl;
}
int main(int argc,char* argv[])
{
    JsonUtilTest();
    return 0;
}

运行结果:
在这里插入图片描述

18、服务器配置信息模块实现–系统配置信息

使用文件配置加载一些程序的运行关键信息可以让程序的运行更加灵活
配置信息

  • 热点判断时间
  • 文件下载URL前缀路径
  • 压缩包后缀名称
  • 上传文件存放路径
  • 压缩文件存放路径
  • 服务端备份信息存放文件
  • 服务器的监听端口
  • 服务器的监听IP

19、服务端配置信息模块实现–单例文件配置类设计

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

-----------------cloud.cpp-----------------------
#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->GetPackDir()<<std::endl;
    std::cout<<config->GetBackDir()<<std::endl;
    std::cout<<config->GetBackupFile()<<std::endl;
}
int main()
{
    ConfigTest();
    return 0;
}
-----------------------util.hpp------------------------
#ifndef __MY_UTIL__
#define __MY_UTIL__
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<experimental/filesystem>
#include<jsoncpp/json/json.h>
#include"bundle.h"

namespace cloud{
    namespace fs = std::experimental::filesystem;
    class FileUtil{
    public:
        FileUtil(const std::string &filename)
        :_filename(filename)
        {}
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
                {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        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 error!\n";
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename,std::ios::binary);
            if(ifs.is_open()==false)
            {
                std::cout<<"open file failed while reading!\n";
                return false;
            }

            ifs.seekg(pos,std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0],len);
            if(ifs.good()==false)
            {
                std::cout<<"get file content failed\n";
            }
            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<<"open file failed while writting!\n";
                return false;
            }
            ofs.write(&body[0],body.size());
            if(ofs.good()==false)
            {
                std::cout<<"write file content failed!\n";
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const std::string &packname)
        {
            //1、获取源文件数据
            std::string body;
            if(this->GetContent(&body)==false)
            {
                std::cout<<"compress get file content failed!\n";
                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!\n";
                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!\n";
                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!\n";
                return false;
            }
            return true;
        }
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool CreateDirectory()
        {
            if(this->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;
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
        std::string _filename;
    };
    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root,std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            if(sw->write(root,&ss)!=0)
            {
                std::cout<<"json write failed!\n";
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str,Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(),str.c_str()+str.size(),root,&err);
            if(ret == false)
            {
                std::cout<<"parse error:"<<err<<std::endl;
                return false;
            }
            return true;
        }
    };
};
#endif
-------------------------Makefile------------------------
cloud:cloud.cpp util.hpp  bundle.cpp
	g++ -o $@ $^  -lpthread -lstdc++fs -ljsoncpp
.PHONY:clean
clean:
	rm -f cloud

运行结果:
在这里插入图片描述

20、服务端模块实现–管理的数据信息

  • 文件实际存储路径(当客户端要下载文件时,则从这个文件中读取数据进行响应)
  • 文件是否压缩标志(判断文件是否被压缩)
  • 压缩包存储路径(如果文件是非热点文件就会被压缩,路径存放的是压缩包,不是源文件,就需要解压缩,然后读取解压后的文件数据)
  • 文件访问URL
  • 文件最后一次修改时间
  • 文件最后一次访问时间
  • 文件大小

21、服务端数据管理模块实现–如何管理数据

  • 内存中以文件访问URL为key,数据信息结构为val,使用哈希表进行管理,查询速度快。使用url作为key是因为往后客户端浏览器下载文件的时候总是以url作为请求
  • 采用文件形式对数据进行持久化存储(序列化方式采用json格式或者自定义方式)

22、服务端数据管理模块实现–数据管理类的设计

由于bundle.cpp文件太大,编译时间太久。这里将bundle生成静态库
在这里插入图片描述

-------------------Makefile----------------------------
cloud:cloud.cpp util.hpp  
	g++  -o $@ $^  -L./lib -lpthread -lstdc++fs -ljsoncpp -lbundle
.PHONY:clean
clean:
	rm -f cloud
--------------data.hpp---------------------------
#ifndef __MY_DATA__
#define ___MY_DATA__
#include<unordered_map>
#include<pthread.h>
#include"util.hpp"
#include"config.hpp"

namespace cloud{
    typedef 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
        bool NewBackupInfo(const std::string &realpath)
        {
            FileUtil fu(realpath);
            if(fu.Exists()==false)
            {
                std::cout<<"new backupinfo file does not exist!"<<std::endl;
                 return false;
            }
            Config *config = Config::GetInstance();
            std::string packdir = config->GetPackDir();
            std::string packfile_suffix = config->GetPackfileSuffix();
            std::string download_prefix = config->GetDownloadPrefix();
            this->pack_flag = false;
            this->fsize = fu.FileSize();
            this->atime = fu.LastATime();
            this->mtime = fu.LastMTime();
            this->real_path = realpath;
            this->pack_path = packdir+fu.FileName()+packfile_suffix;
            this->url = download_prefix+fu.FileName();
            return true;
        }
    }BackupInfo;
}
#endif
----------------------Config.hpp------------------------
#ifndef __MY_DATA__
#define ___MY_DATA__
#include<unordered_map>
#include<pthread.h>
#include"util.hpp"
#include"config.hpp"

namespace cloud{
    typedef 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
        bool NewBackupInfo(const std::string &realpath)
        {
            FileUtil fu(realpath);
            if(fu.Exists()==false)
            {
                std::cout<<"new backupinfo file does not exist!"<<std::endl;
                 return false;
            }
            Config *config = Config::GetInstance();
            std::string packdir = config->GetPackDir();
            std::string packfile_suffix = config->GetPackfileSuffix();
            std::string download_prefix = config->GetDownloadPrefix();
            this->pack_flag = false;
            this->fsize = fu.FileSize();
            this->atime = fu.LastATime();
            this->mtime = fu.LastMTime();
            this->real_path = realpath;
            this->pack_path = packdir+fu.FileName()+packfile_suffix;
            this->url = download_prefix+fu.FileName();
            return true;
        }
    }BackupInfo;
}
#endif
--------------------------cloud.cpp-------------------------
#include"config.hpp"
#include"data.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->GetPackDir()<<std::endl;
    std::cout<<config->GetBackDir()<<std::endl;
    std::cout<<config->GetBackupFile()<<std::endl;
}
void DataTest1(const std::string &filename)
{
    cloud::BackupInfo info;
    info.NewBackupInfo(filename);
    std::cout<<"pack_flag:"<<info.pack_flag<<std::endl;
    std::cout<<"fsize:"<<info.fsize<<std::endl;
    std::cout<<"atime:"<<info.atime<<std::endl;
    std::cout<<"mtime:"<<info.mtime<<std::endl;
    std::cout<<"real_path:"<<info.real_path<<std::endl;
    std::cout<<"pack_path:"<<info.pack_path<<std::endl;
    std::cout<<"url:"<<info.url<<std::endl;
}
int main(int argc,char* argv[])
{
    // ConfigTest();
    DataTest1(argv[1]);
    return 0;
}
-----------------------------cloud.conf-------------------------
{
    "hot_time" : 30,
    "server_port" : 9090,
    "server_ip" : "106.54.9.13",
    "download_prefix" : "/download/",
    "packfile_suffix" : ".lz",
    "pack_dir" : "./packdir/",
    "back_dir" : "./backdir",
    "backup_file" : "./cloud.dat"
}

-----------------------util.hpp---------------------------------
#ifndef __MY_UTIL__
#define __MY_UTIL__
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<experimental/filesystem>
#include<jsoncpp/json/json.h>
#include"bundle.h"

namespace cloud{
    namespace fs = std::experimental::filesystem;
    class FileUtil{
    public:
        FileUtil(const std::string &filename)
        :_filename(filename)
        {}
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
                {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        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 error!\n";
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename,std::ios::binary);
            if(ifs.is_open()==false)
            {
                std::cout<<"open file failed while reading!\n";
                return false;
            }

            ifs.seekg(pos,std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0],len);
            if(ifs.good()==false)
            {
                std::cout<<"get file content failed\n";
            }
            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<<"open file failed while writting!\n";
                return false;
            }
            ofs.write(&body[0],body.size());
            if(ofs.good()==false)
            {
                std::cout<<"write file content failed!\n";
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const std::string &packname)
        {
            //1、获取源文件数据
            std::string body;
            if(this->GetContent(&body)==false)
            {
                std::cout<<"compress get file content failed!\n";
                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!\n";
                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!\n";
                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!\n";
                return false;
            }
            return true;
        }
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool CreateDirectory()
        {
            if(this->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;
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
        std::string _filename;
    };
    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root,std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            if(sw->write(root,&ss)!=0)
            {
                std::cout<<"json write failed!\n";
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str,Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(),str.c_str()+str.size(),root,&err);
            if(ret == false)
            {
                std::cout<<"parse error:"<<err<<std::endl;
                return false;
            }
            return true;
        }
    };
};
#endif

运行结果:
在这里插入图片描述

23、服务端数据管理模块实现–数据管理类的设计

-----------------------util.hpp---------------------------------
#ifndef __MY_UTIL__
#define __MY_UTIL__
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<experimental/filesystem>
#include<jsoncpp/json/json.h>
#include"bundle.h"

namespace cloud{
    namespace fs = std::experimental::filesystem;
    class FileUtil{
    public:
        FileUtil(const std::string &filename)
        :_filename(filename)
        {}
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
                {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        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 error!\n";
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename,std::ios::binary);
            if(ifs.is_open()==false)
            {
                std::cout<<"open file failed while reading!\n";
                return false;
            }

            ifs.seekg(pos,std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0],len);
            if(ifs.good()==false)
            {
                std::cout<<"get file content failed\n";
            }
            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<<"open file failed while writting!\n";
                return false;
            }
            ofs.write(&body[0],body.size());
            if(ofs.good()==false)
            {
                std::cout<<"write file content failed!\n";
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const std::string &packname)
        {
            //1、获取源文件数据
            std::string body;
            if(this->GetContent(&body)==false)
            {
                std::cout<<"compress get file content failed!\n";
                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!\n";
                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!\n";
                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!\n";
                return false;
            }
            return true;
        }
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool CreateDirectory()
        {
            if(this->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;
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
        std::string _filename;
    };
    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root,std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            if(sw->write(root,&ss)!=0)
            {
                std::cout<<"json write failed!\n";
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str,Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(),str.c_str()+str.size(),root,&err);
            if(ret == false)
            {
                std::cout<<"parse error:"<<err<<std::endl;
                return false;
            }
            return true;
        }
    };
};
#endif
-------------------Makefile----------------------------
cloud:cloud.cpp util.hpp  
	g++  -o $@ $^  -L./lib -lpthread -lstdc++fs -ljsoncpp -lbundle
.PHONY:clean
clean:
	rm -f cloud
--------------data.hpp---------------------------
#ifndef __MY_DATA__
#define ___MY_DATA__
#include<unordered_map>
#include<pthread.h>
#include"util.hpp"
#include"config.hpp"

namespace cloud{
    typedef 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
        bool NewBackupInfo(const std::string &realpath)
        {
            FileUtil fu(realpath);
            if(fu.Exists()==false)
            {
                std::cout<<"new backupinfo file does not exist!"<<std::endl;
                 return false;
            }
            Config *config = Config::GetInstance();
            std::string packdir = config->GetPackDir();
            std::string packfile_suffix = config->GetPackfileSuffix();
            std::string download_prefix = config->GetDownloadPrefix();
            this->pack_flag = false;
            this->fsize = fu.FileSize();
            this->atime = fu.LastATime();
            this->mtime = fu.LastMTime();
            this->real_path = realpath;
            this->pack_path = packdir+fu.FileName()+packfile_suffix;
            this->url = download_prefix+fu.FileName();
            return true;
        }
    }BackupInfo;
    class DataManager
    {
    public:
        DataManager()
        {
            _backup_file = Config::GetInstance()->GetBackupFile();
            pthread_rwlock_init(&_rwlock,NULL);

        }
        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;
        }
        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;
            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> *array)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            for(;it!=_table.end();++it)
            {
                array->push_back(it->second);
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock);
        }
    private:
        std::string _backup_file;
        pthread_rwlock_t _rwlock;
        std::unordered_map<std::string,BackupInfo> _table;
    };
}
#endif
----------------------Config.hpp------------------------
#ifndef __MY_DATA__
#define ___MY_DATA__
#include<unordered_map>
#include<pthread.h>
#include"util.hpp"
#include"config.hpp"

namespace cloud{
    typedef 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
        bool NewBackupInfo(const std::string &realpath)
        {
            FileUtil fu(realpath);
            if(fu.Exists()==false)
            {
                std::cout<<"new backupinfo file does not exist!"<<std::endl;
                 return false;
            }
            Config *config = Config::GetInstance();
            std::string packdir = config->GetPackDir();
            std::string packfile_suffix = config->GetPackfileSuffix();
            std::string download_prefix = config->GetDownloadPrefix();
            this->pack_flag = false;
            this->fsize = fu.FileSize();
            this->atime = fu.LastATime();
            this->mtime = fu.LastMTime();
            this->real_path = realpath;
            this->pack_path = packdir+fu.FileName()+packfile_suffix;
            this->url = download_prefix+fu.FileName();
            return true;
        }
    }BackupInfo;
    class DataManager
    {
    public:
        DataManager()
        {
            _backup_file = Config::GetInstance()->GetBackupFile();
            pthread_rwlock_init(&_rwlock,NULL);

        }
        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;
        }
        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;
            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> *array)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            for(;it!=_table.end();++it)
            {
                array->push_back(it->second);
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock);
        }
    private:
        std::string _backup_file;
        pthread_rwlock_t _rwlock;
        std::unordered_map<std::string,BackupInfo> _table;
    };
}
#endif
--------------------------cloud.cpp-------------------------
#include"config.hpp"
#include"data.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->GetPackDir()<<std::endl;
    std::cout<<config->GetBackDir()<<std::endl;
    std::cout<<config->GetBackupFile()<<std::endl;
}
void DataTest1(const std::string &filename)
{
    cloud::BackupInfo info;
    info.NewBackupInfo(filename);
    std::cout<<"pack_flag:"<<info.pack_flag<<std::endl;
    std::cout<<"fsize:"<<info.fsize<<std::endl;
    std::cout<<"atime:"<<info.atime<<std::endl;
    std::cout<<"mtime:"<<info.mtime<<std::endl;
    std::cout<<"real_path:"<<info.real_path<<std::endl;
    std::cout<<"pack_path:"<<info.pack_path<<std::endl;
    std::cout<<"url:"<<info.url<<std::endl;
}
void DataTest2(const std::string &filename)
{
    cloud::BackupInfo info;
    info.NewBackupInfo(filename);
    cloud::DataManager data;
    data.Insert(info);

    cloud::BackupInfo tmp;
    data.GetOneByURL("/download/bundle.h",&tmp);
    std::cout<<"---------------------Insert&GetOneByURL--------------------------"<<std::endl;
    std::cout<<"pack_flag:"<<tmp.pack_flag<<std::endl;
    std::cout<<"fsize:"<<tmp.fsize<<std::endl;
    std::cout<<"mtime:"<<tmp.mtime<<std::endl;
    std::cout<<"atime:"<<tmp.atime<<std::endl;
    std::cout<<"real_path:"<<tmp.real_path<<std::endl;
    std::cout<<"pack_path:"<<tmp.pack_path<<std::endl;
    std::cout<<"url:"<<tmp.url<<std::endl;

    std::cout<<"---------------------Update&GetAll--------------------------"<<std::endl;

    info.pack_flag = true;
    data.Update(info);
    std::vector<cloud::BackupInfo> array;
    data.GetAll(&array);

    for(auto &a:array)
    {
        std::cout<<"pack_flag:"<<a.pack_flag<<std::endl;
        std::cout<<"fsize:"<<a.fsize<<std::endl;
        std::cout<<"mtime:"<<a.mtime<<std::endl;
        std::cout<<"atime:"<<a.atime<<std::endl;
        std::cout<<"real_path:"<<a.real_path<<std::endl;
        std::cout<<"pack_path:"<<a.pack_path<<std::endl;
        std::cout<<"url:"<<a.url<<std::endl;
    }
    std::cout<<"---------------------GetOneByRealPath--------------------------"<<std::endl;

    data.GetOneByRealPath(filename,&tmp);
    std::cout<<"pack_flag:"<<tmp.pack_flag<<std::endl;
    std::cout<<"fsize:"<<tmp.fsize<<std::endl;
    std::cout<<"mtime:"<<tmp.mtime<<std::endl;
    std::cout<<"atime:"<<tmp.atime<<std::endl;
    std::cout<<"real_path:"<<tmp.real_path<<std::endl;
    std::cout<<"pack_path:"<<tmp.pack_path<<std::endl;
    std::cout<<"url:"<<tmp.url<<std::endl;


}
int main(int argc,char* argv[])
{
    // ConfigTest();
    // DataTest1(argv[1]);
    DataTest2(argv[1]);
    return 0;
}
-----------------------------cloud.conf-------------------------
{
    "hot_time" : 30,
    "server_port" : 9090,
    "server_ip" : "106.54.9.13",
    "download_prefix" : "/download/",
    "packfile_suffix" : ".lz",
    "pack_dir" : "./packdir/",
    "back_dir" : "./backdir",
    "backup_file" : "./cloud.dat"
}

运行结果:
在这里插入图片描述
InitLoad

-----------------cloud.cpp---------------------
#include"config.hpp"
#include"data.hpp"

void DataTest2(const std::string &filename)
{
    cloud::BackupInfo info;
    info.NewBackupInfo(filename);
    cloud::DataManager data;
    data.Insert(info);

    cloud::BackupInfo tmp;
    data.GetOneByURL("/download/bundle.h",&tmp);
    std::cout<<"---------------------Insert&GetOneByURL--------------------------"<<std::endl;
    std::cout<<"pack_flag:"<<tmp.pack_flag<<std::endl;
    std::cout<<"fsize:"<<tmp.fsize<<std::endl;
    std::cout<<"mtime:"<<tmp.mtime<<std::endl;
    std::cout<<"atime:"<<tmp.atime<<std::endl;
    std::cout<<"real_path:"<<tmp.real_path<<std::endl;
    std::cout<<"pack_path:"<<tmp.pack_path<<std::endl;
    std::cout<<"url:"<<tmp.url<<std::endl;

    std::cout<<"---------------------Update&GetAll--------------------------"<<std::endl;

    info.pack_flag = true;
    data.Update(info);
    std::vector<cloud::BackupInfo> array;
    data.GetAll(&array);

    for(auto &a:array)
    {
        std::cout<<"pack_flag:"<<a.pack_flag<<std::endl;
        std::cout<<"fsize:"<<a.fsize<<std::endl;
        std::cout<<"mtime:"<<a.mtime<<std::endl;
        std::cout<<"atime:"<<a.atime<<std::endl;
        std::cout<<"real_path:"<<a.real_path<<std::endl;
        std::cout<<"pack_path:"<<a.pack_path<<std::endl;
        std::cout<<"url:"<<a.url<<std::endl;
    }
    std::cout<<"---------------------GetOneByRealPath--------------------------"<<std::endl;

    data.GetOneByRealPath(filename,&tmp);
    std::cout<<"pack_flag:"<<tmp.pack_flag<<std::endl;
    std::cout<<"fsize:"<<tmp.fsize<<std::endl;
    std::cout<<"mtime:"<<tmp.mtime<<std::endl;
    std::cout<<"atime:"<<tmp.atime<<std::endl;
    std::cout<<"real_path:"<<tmp.real_path<<std::endl;
    std::cout<<"pack_path:"<<tmp.pack_path<<std::endl;
    std::cout<<"url:"<<tmp.url<<std::endl;

    data.Storage();


}
int main(int argc,char* argv[])
{
    DataTest2(argv[1]);
    return 0;
}
-------------------data.hpp--------------
#ifndef __MY_DATA__
#define ___MY_DATA__
#include<unordered_map>
#include<pthread.h>
#include"util.hpp"
#include"config.hpp"

namespace cloud{
    typedef 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
        bool NewBackupInfo(const std::string &realpath)
        {
            FileUtil fu(realpath);
            if(fu.Exists()==false)
            {
                std::cout<<"new backupinfo file does not exist!"<<std::endl;
                 return false;
            }
            Config *config = Config::GetInstance();
            std::string packdir = config->GetPackDir();
            std::string packfile_suffix = config->GetPackfileSuffix();
            std::string download_prefix = config->GetDownloadPrefix();
            this->pack_flag = false;
            this->fsize = fu.FileSize();
            this->atime = fu.LastATime();
            this->mtime = fu.LastMTime();
            this->real_path = realpath;
            this->pack_path = packdir+fu.FileName()+packfile_suffix;
            this->url = download_prefix+fu.FileName();
            return true;
        }
    }BackupInfo;
    class DataManager
    {
    public:
        DataManager()
        {
            _backup_file = Config::GetInstance()->GetBackupFile();
            pthread_rwlock_init(&_rwlock,NULL);

        }
        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;
        }
        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;
            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> *array)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            for(;it!=_table.end();++it)
            {
                array->push_back(it->second);
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
        bool Storage()
        {
            //1、获取所有数据
            std::vector<BackupInfo>array;
            this->GetAll(&array);
            //2、添加到Json::Value
            Json::Value root;
            for(int i = 0;i<array.size();i++)
            {
                Json::Value item;
                item["pack_flag"] = array[i].pack_flag;
                item["fsize"] = (Json::Int64)array[i].fsize;
                item["atime"] = (Json::Int64)array[i].atime;
                item["mtime"] = (Json::Int64)array[i].mtime;
                item["real_path"] = array[i].real_path;
                item["pack_path"] = array[i].pack_path;
                item["url"] = array[i].url;
                root.append(item);
            }
            //3、序列化
            std::string body;
            JsonUtil::Serialize(root,&body);
            //4、写文件
            FileUtil fu(_backup_file);
            fu.SetContent(body);
            return true;
        }
        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock);
        }
    private:
        std::string _backup_file;
        pthread_rwlock_t _rwlock;
        std::unordered_map<std::string,BackupInfo> _table;
    };
}
#endif

运行结果:
在这里插入图片描述

----------------cloud.cpp-------------
#include"config.hpp"
#include"data.hpp"
void DataTest3(const std::string &filename)
{
    cloud::DataManager data;
    std::vector<cloud::BackupInfo> array;
    data.GetAll(&array);
    for(auto &a:array)
    {
        std::cout<<"pack_flag:"<<a.pack_flag<<std::endl;
        std::cout<<"fsize:"<<a.fsize<<std::endl;
        std::cout<<"mtime:"<<a.mtime<<std::endl;
        std::cout<<"atime:"<<a.atime<<std::endl;
        std::cout<<"real_path:"<<a.real_path<<std::endl;
        std::cout<<"pack_path:"<<a.pack_path<<std::endl;
        std::cout<<"url:"<<a.url<<std::endl;
    }
}
int main(int argc,char* argv[])
{
    DataTest3(argv[1]);
    return 0;
}

运行结果:
在这里插入图片描述

24、服务端热点管理模块实现–热点管理实现思路

服务端的热点文件管理是对上传的非热点文件进行压缩存储,节省磁盘空间
而热点文件的判断在于上传的文件的最后一次访问时间是否在热点判断时间之内,比如如果一个文件在热点判断时间之内都没有被访问过,我们就认为这是一个非热点文件,其实就是当前系统时间,与文件最后一次访问时间之间的时间差是否在热点判断时间之内的判断

而我们需要对上传的文件每隔一段时间进行热点检测,相当于遍历上传文件的存储文件夹,找出所有的文件,然后通过对逐个文件进行时间差的判断,来逐个进行热点处理
基于这个思想,我们需要将上传的文件存储位置与压缩后压缩文件的存储位置分开。这样在遍历上传文件夹的时候不至于将压缩过的文件又进行非热点处理了。

  • 上传文件有自己的上传存储位置,非热点文件的压缩存储有自己的存储位置
  • 遍历上传存储位置文件夹,获取所有文件信息
  • 获取每个文件最后一次访问时间,进而完成是否是热点文件的判断
  • 对非热点文件进行压缩存储,删除源文件
  • 修改数据管理模块对应的文件信息(压缩标志)
    热点管理实现
---------------hot.hpp----------------
#ifndef __MY_HOT__
#define __MY_HOT__
#include<unistd.h>
#include"data.hpp"

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 RunModule()
        {
            while(1)
            {
                //1、遍历备份目录、获取所有文件名
                FileUtil fu(_back_dir);
                std::vector<std::string> array;
                fu.ScanDirectory(&array);
                //2、遍历判断文件是否是非热点文件
                for(auto &a:array)
                {
                    //是热点文件不作处理
                    if(HotJudge(a)==true)
                    {
                        continue;
                    }
                    //获取文件备份信息
                    BackupInfo bi;
                    if(_data->GetOneByRealPath(a,&bi)==false)
                    {
                        bi.NewBackupInfo(a);
                    }
                    //3、对非热点文件进行压缩处理
                    FileUtil tmp(a);
                    tmp.Compress(bi.pack_path);
                    //4、删除源文件,修改备份信息
                    tmp.Remove();
                    bi.pack_flag = true;
                    _data->Update(bi);
                }
                return true;
            }
            usleep(1000); 
        }
    private:
        std::string _back_dir;//备份文件路径
        std::string _pack_dir;//压缩文件路径
        std::string _pack_suffix;//压缩包后缀名
        int _hot_time;//热点判断时间  
    private:
        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;
        }    
    };
} 

#endif
--------------cloud.cpp---------------
#include"config.hpp"
#include"data.hpp"
#include"hot.hpp"
cloud::DataManager *_data;
void HotTest()
{
    _data = new cloud::DataManager();
    cloud::HotManager hot;
    hot.RunModule();
}
int main(int argc,char* argv[])
{
    HotTest();
    return 0;
}
---------------util.hpp-------------
#ifndef __MY_UTIL__
#define __MY_UTIL__
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<experimental/filesystem>
#include<jsoncpp/json/json.h>
#include"bundle.h"

namespace cloud{
    namespace fs = std::experimental::filesystem;
    class FileUtil{
    public:
        FileUtil(const std::string &filename)
        :_filename(filename)
        {}
        bool Remove()
        {
            if(this->Exists()==false)
            {
                return false;
            }
            remove(_filename.c_str());
            return true;
        }
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
                {
             struct stat st;
            if(stat(_filename.c_str(),&st)<0)
            {
                std::cout<<"getfile size failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        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 error!\n";
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename,std::ios::binary);
            if(ifs.is_open()==false)
            {
                std::cout<<"open file failed while reading!\n";
                return false;
            }

            ifs.seekg(pos,std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0],len);
            if(ifs.good()==false)
            {
                std::cout<<"get file content failed\n";
            }
            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<<"open file failed while writting!\n";
                return false;
            }
            ofs.write(&body[0],body.size());
            if(ofs.good()==false)
            {
                std::cout<<"write file content failed!\n";
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const std::string &packname)
        {
            //1、获取源文件数据
            std::string body;
            if(this->GetContent(&body)==false)
            {
                std::cout<<"compress get file content failed!\n";
                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!\n";
                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!\n";
                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!\n";
                return false;
            }
            return true;
        }
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool CreateDirectory()
        {
            if(this->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;
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
        std::string _filename;
    };
    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root,std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            if(sw->write(root,&ss)!=0)
            {
                std::cout<<"json write failed!\n";
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str,Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(),str.c_str()+str.size(),root,&err);
            if(ret == false)
            {
                std::cout<<"parse error:"<<err<<std::endl;
                return false;
            }
            return true;
        }
    };
};
#endif

运行结果:
在这里插入图片描述

25、服务端业务处理模块实现–业务处理实现思路

云备份项目中,业务处理模块是针对客户端请求进行处理,并最终给予相应。而整个过程中包含以下要实现的功能:

  • 借助网络通信模块httplib库搭建http服务器与客户端进行网络通信
  • 针对收到的请求进行对应的业务处理并进行响应(文件上传、列表查看、文件下载(包括断点续传))

26、服务端业务处理模块实现–网络通信接口设计

业务处理模块要针对客户端的请求进行处理,那么我们就需要提前定义好客户端与服务端的通信,明确客户端发送什么样的请求,服务端处理后应该给予什么样的响应,而这就是网络通信接口的设计

  • HTTP文件上传

POST /upload HTTP/1.1
Content-Length:11
Content-Type:multipart/form-data;boundary=----WebKitFormBoundary+16字节随机字符
----WebKitFormBoundary–
Content-Disposition:form-data;filename=“a.txt”;
hello world

-----WebKitFormBoundary–

HTTP/1.1 200 OK
Content-Length:0

  • HTTP文件列表获取

GET /list HTTP/1.1
Content-Length:0

HTTP /1.1 200 OK
Content-Length:0

  • HTTP文件下载

GET /download/a.txt http/1.1
Content-Length:0

HTTP/1.1 200 OK
Content-Length:100000
ETags:"filename-size-t-mtime 一个能够唯一标识文件的数据
Accept-Ranges:bytes
文件数据

http的ETag头部字段:其中存储了一个资源的唯一标识,客户端第一次下载文件的时候,会收到这个响应信息,第二次下载,就会将这个信息发送给服务器,想要让服务器根据这个唯一标识判断这个资源有没有被修改过,如果没有被修改过。直接使用原先缓存的数据,不用重新下载。
http协议本身对于etag中是什么数据并不关心,只要你服务端能够自己标识就行。因此我们etag就用“文件名-文件大小-最后一次修改时间”组成,而etag字段不仅仅是缓存用到,还有就是后边的断点续传的实现也要实现。
http协议的Accept-Ranges:bytes字段:用于高速客户端我支持断点续传,并且数据单位以字节作为单位
Content-Type的重要性

  • HTTP断点续传

GET /download/a.txt http/1.1
Content-Length:0
If-Range:服务器在下载时相应的etag字段(用于服务端判断这个文件与原先下载的文件是否一致)
Range:bytes=89-999 这个字段用于高速服务器需要的数据区间范围

HTTP/1.1 206 Partial Content(服务器处理了部分GET请求)
Etag:“fjsajkd”
Content-Range:bytes 89-999 (文件大小)
正文数据

断点续传,功能:当文件下载过程中,因为某种异常而中断,如果再次进行从头下载,效率较低,因为需要将之前以前已经传输过的数据再次传输一遍。因此断点续传就是从上次下载断开的位置重新下载即可,之前已经传输过的数据将不需要再重新传输
目的:提高文件重新传输效率
实现思想:客户端在下载文件的时候,要每次接受到数据写入文件后记录自己当前下载的数据量。当异常下载中断时,下次断点续传的时候,将要下载的数据区间(下载起始位置,结束位置)发送给服务器,服务器收到后,仅仅回传客户端需要的区间数据即可。
注:如果上次下载文件之后,这个文件在服务器上被修改了,则这时候将不能断点续传,而是应该重新进行文件下载操作。

27、服务端业务处理模块实现–业务处理类设计

#ifndef __MY_SERVICE__
#define __MY_SERVICE__

#include"data.hpp"
#include"httplib.h"
#include"util.hpp"

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");
            //file.filename;//文件名称  file.content //文件数据
            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);//向数据管理模块添加备份的文件信息
            return;
        }
        static void ListShow(const httplib::Request &req,httplib::Response &rsp)
        {   
            //1、获取所有的文件备份信息
            std::vector<BackupInfo> array;
            _data->GetAll(&array);
            //2、根据所有备份信息,组织html文件数据
            std::stringstream ss;
            ss<<"<html><head><title>Download</title></head>";
            ss<<"<body><h1>Download</h1><table>";
            for(auto &a:array)
            {
                ss<<"<tr>";
                std::string filename = FileUtil(a.real_path).FileName();
                ss<<"<td><a href='"<<a.url<<"'>"<<filename<<"</a></td>";
                ss<<"<td align='right'>"<<TimetoStr(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;
            return;

        }
        static std::string GetEtag(const BackupInfo &info)
        {
            // ETags:"filename-size-mtime
            FileUtil fu(info.real_path);
            std::string etag = fu.FileName();
            etag+="-";
            etag+=std::to_string(fu.FileSize());
            etag+="-";
            etag+=std::to_string(fu.LastMTime());
            return etag;
        }
        static void Download(const httplib::Request &req,httplib::Response &rsp)
        {
            //1、获取客户端请求的资源路径path
            BackupInfo info;
            //2、根据资源路径,获取文件备份信息
            _data->GetOneByURL(req.path,&info);
            //3、判断文件是否被压缩,如果被压缩,要先解压缩
            if(info.pack_flag == true)
            {
                FileUtil fu(info.pack_path);
                fu.UnCompress(info.real_path);
                //3、1 删除压缩包,修改备份信息(已经没有被压缩)
                fu.Remove();
                info.pack_flag = false;
                _data ->Update(info);
            }
            //4、读取文件数据,放入rsp.body中
            FileUtil fu(info.real_path);
            bool retrans = false;//标记当前是否是断点续传
            std::string old_etag ;
            if(req.has_header("If-Range"))
            {
                old_etag = req.get_header_value("If-Range");
                //如果没有If-Range字段,则是正常下载,或者如果有这个字段,但是它的值
                //与当前文件的etag不一致,则也必须重新返回全部数据
                if(old_etag == GetEtag(info))
                retrans = true;
            }
            if(retrans == false)
            {
                //httplib内部实现了对于区间请求也就是断点续传请求的处理
                //会根据请求的范围进行处理,从body中截取指定范围的数据进行客户端的响应
                fu.GetContent(&rsp.body);
                //5、设置响应头部,ETags,Accept-Ranges:bytes
                rsp.set_header("Accept-Ranges","bytes");
                rsp.set_header("ETag",GetEtag(info));
                rsp.set_header("Content-Type","application/octet-stream");
                rsp.status = 200;
            }
            else
            {
                //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_Range","bytes start-end/fsize")
                rsp.status = 206;//区间请求成功响应
            }
        }

    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(_server_ip,_server_port);
            return true;
        }
    private:
        int _server_port ;
        std::string _server_ip ;
        std::string _download_prefix;
        httplib::Server _server;
    };
}
#endif
#include"config.hpp"
#include"data.hpp"
#include"hot.hpp"
#include"service.hpp"
void ServiceTest()
{
    cloud::Service srv;
    srv.RunModule();
}
int main(int argc,char* argv[])
{
    _data = new cloud::DataManager();
    ServiceTest();
    return 0;
}

运行结果:
1、打开本地编写的html文件

<html>
    <head>
        <title>Download</title>
    </head>
    <body>
       <form action="http://106.54.9.13:9090/upload" method="post" enctype="multipart/form-data">
            <div>
                <input type="file" name = "file">
            </div>
            <div><input type="submit" value="上传"></div>
       </form>
    </body>
</html>

选择上传exercise.txt和hello.txt文件
在这里插入图片描述2、在地址栏输入我云服务器的公网IP地址+端口号
在这里插入图片描述
可以看到文件上传成功
同时listshow请求是发送成功,并且成功响应的
3、点击exercise.txt发起下载请求
在这里插入图片描述

28、客户端数据管理模块实现–数据信息设计

客户端要实现的功能是对指定文件夹中的文件自动进行备份上传。但是并不是所有的文件每次都需要上传,我们需要能够判断,那些文件需要上传,那些不需要,因此需要将备份的文件信息给管理起来,作为下一次文件是否需要备份的判断。
因此需要被管理的信息包含以下:

  • 文件路径名称
  • 文件唯一标识:由文名,最后一次修改时间,文件大小组成的一串信息

模块划分:

数据管理模块:管理备份的文件信息

  • 其中的信息用来判断文件是否需要重新备份

    1、文件是否是新增的
    2、如果不是新增的,那么上次备份过后是否修改

  • 实现思路:
    1、内存存储:高访问效率(哈希表)
    2、持久化存储:文件存储(序列化)

目录遍历模块:获取指定文件夹中的所有文件路径名
文件备份模块:将需要备份的文件上传备份到服务器

29、客户端文件检测模块实现–文件操作实用类设计

客户端部署在windows环境下,在这里我们不采用json格式进行序列化反序列化。客户端文件操作实用类和我们前面编写的服务器端的文件操作类需要实现的功能甚至更少,如下:

#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING 1
#ifndef __MY_UTIL__
#define __MY_UTIL__
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<experimental/filesystem>


namespace cloud {
    namespace fs = std::experimental::filesystem;
    class FileUtil {
    public:
        FileUtil(const std::string& filename)
            :_filename(filename)
        {}
        bool Remove()
        {
            if (this->Exists() == false)
            {
                return false;
            }
            remove(_filename.c_str());
            return true;
        }
        size_t FileSize()
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "getfile size failed!\n";
                //return -1;
                return 0;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "getfile size failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "getfile size failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        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 error!\n";
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename, std::ios::binary);
            if (ifs.is_open() == false)
            {
                std::cout << "open file failed while reading!\n";
                return false;
            }

            ifs.seekg(pos, std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0], len);
            if (ifs.good() == false)
            {
                std::cout << "get file content failed\n";
            }
            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 << "open file failed while writting!\n";
                return false;
            }
            ofs.write(&body[0], body.size());
            if (ofs.good() == false)
            {
                std::cout << "write file content failed!\n";
                return false;
            }
            ofs.close();
            return true;
        }
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool CreateDirectory()
        {
            if (this->Exists()) return true;
            return fs::create_directories(_filename);
        }
        bool ScanDirectory(std::vector<std::string>* array)
        {
            this->CreateDirectory();
            for (auto& p : fs::directory_iterator(_filename))
            {
                if (fs::is_directory(p) == true)   continue;
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
        std::string _filename;
    };
};
#endif

30、客户端数据管理模块实现–数据管理类设计

自动将指定文件夹中的文件备份到服务器

流程

  • 遍历指定文件夹,获取文件信息
  • 逐一判断文件是否需要备份
  • 需要备份的文件进行上传备份

31、项目总结

项目名称:云备份系统
项目功能:搭建云备份服务器与客户端,客户端程序运行在客户机上自动将指定目录下的文件备份到服务器,并且能够支持浏览器查看与下载,其中下载支持断点续传功能,并且服务器对备份的文件进行热点管理,将长时间无访问文件进行压缩存储。
开发环境:centos7.6/vim、g++、makefile、windows11、vs2019
技术特点:http客户端/服务器搭建,json序列化,文件压缩,热点管理,断点续传,线程池,读写锁,单例模式
项目模块

  • 服务端

    • 1、数据管理模块:内存中使用hash表存储提高访问效率,持久化使用文件存储管理备份数据
    • 2、业务处理模块:搭建http服务器与客户端进行通信处理客户端的上传、下载、查看请求、支持断点续传
    • 3、热点管理模块:对备份的文件进行热点管理,将长时间无访问文件进行压缩处理,节省磁盘空间。
  • 客户端

    • 1、数据管理模块:内存中使用hash表存储提高访问效率,持久化使用文件存储管理备份数据
    • 2、文件检索模块:基于c++17文件系统库,遍历获取指定文件夹下所有文件
    • 3、文件备份模块:搭建http客户端上传备份文件

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

openGauss 6.0.0 一主二备集群安装及使用zcbus实现Oracle到openGauss的数据同步

一、前言 openGauss 6.0.0-RC1是openGauss 2024年3月发布的创新版本&#xff0c;该版本生命周期为0.5年。根据openGauss官网介绍&#xff0c;6.0.0-RC1与之前的版本特性功能保持兼容,另外&#xff0c;在和之前版本兼容的基础上增加了很多新功能&#xff0c;比如分区表性能优化…

Java集合自测题

文章目录 一、说说 List , Set , Map 三者的区别&#xff1f;二、List , Set , Map 在 Java 中分别由哪些对应的实现类&#xff1f;底层的数据结构&#xff1f;三、有哪些集合是线程不安全的&#xff1f;怎么解决呢&#xff1f;四、HashMap 查询&#xff0c;删除的时间复杂度五…

autosleep框架设计与实现

在低功耗系统中&#xff0c;autosleep是一个较小的模块&#xff0c;是低功耗主流程的入口。在Linux内核中&#xff0c;autosleep是休眠流程的触发点和入口点&#xff0c;PM Core的休眠流程入口pm_suspend()就是被autosleep的睡眠工作队列调用而进入休眠的。 该功能的支持受宏…

MyBatis 参数上的处理的细节内容

1. MyBatis 参数上的处理的细节内容 文章目录 1. MyBatis 参数上的处理的细节内容2. MyBatis 参数上的处理3. 准备工作4. 单个(一个)参数4.1 单个(一个)简单类型作为参数4.2 单个(一个) Map集合 作为参数4.3 单个(一个) 实体类POJO作为参数 5. 多个参数5.1 Param注解(命名参数)…

计算机相关专业的探讨

目录 一、计算机相关专业是否仍是“万金油”选择 二、计算机行业的未来发展态势 三、从专业与个人的匹配度判断选择计算机相关专业 四、对于高考生的建议 一、计算机相关专业是否仍是“万金油”选择 计算机相关专业在过去很长一段时间内确实被视为“万金油”专业&#xff0…

算法训练营day06--242.有效的字母异位词+349. 两个数组的交集+202. 快乐数+1. 两数之和

一、242.有效的字母异位词 题目链接&#xff1a;https://leetcode.cn/problems/valid-anagram/description/ 文章讲解&#xff1a;https://programmercarl.com/0242.%E6%9C%89%E6%95%88%E7%9A%84%E5%AD%97%E6%AF%8D%E5%BC%82%E4%BD%8D%E8%AF%8D.html 视频讲解&#xff1a;http…

电视剧推荐

1、《春色寄情人》 2、《唐朝诡事录》 3、《南来北往》 4、《与凤行》 5、《利剑玫瑰》 6、《承欢记》

【教程】使用立创EDA打开JSON格式的PCB及原理图

这里写目录标题 一、将PCB和原理图放同一文件夹二、打开嘉立创EDA并导入.zip文件三、选择.zip文件并选择 “导入文件并提取库” 一、将PCB和原理图放同一文件夹 并打包成.zip文件 二、打开嘉立创EDA并导入.zip文件 嘉立创 我这里用的网页端&#xff0c;客户端下载页面拉到…

html的网页制作代码分享

<!-- prj_8_2.html --> <!DOCTYPE html> <html lang "EN"><head><meta charset"utf-8" /><title>页面布局设计</title><style type "text/css">*{padding: 0px;margin:0px;}#header{back…

Spring中IOC容器

IoC IOC容器 IoC是一种设计思想&#xff0c;面向对象编程 Spring通过IoC管理所有Java对象的实例化和初始化&#xff0c;控制对象之间依赖关系 将IoC容器管理的Java对象称为Spring Bean&#xff0c;与new创建的对象没有区别 控制反转&#xff08;IoC Inversion of Controle&a…

世优科技AI数字人多模态交互系统“世优波塔”正式发布

2024年6月6日&#xff0c;世优科技“波塔发布会”在北京举办&#xff0c;本次发布会上&#xff0c;世优科技以全新的“波塔”产品诠释了更高效、更智能、更全面的AI数字人产品及软硬件全场景解决方案&#xff0c;实现了世优品牌、产品和价值的全面跃迁。来自行业协会、数字产业…

大众点评全国丽人POI采集225万家-2024年5月底

大众点评全国丽人POI采集225万家-2024年5月底 店铺POI点位示例&#xff1a; 店铺id Hav6zIYtzhyyopIZ 店铺名称 防屏蔽 十分制服务评分 8.9 十分制环境评分 8.9 十分制划算评分 8.9 人均价格 210 评价数量 19935 店铺地址 建北一支路观音桥步行街红鼎国际A座9-9 店铺…

英伟达GPU对比分析:A100、A800、H100与H800

在当今技术迅速发展的时代&#xff0c;英伟达的GPU产品线提供了多种高性能选项&#xff0c;以满足不同类型的工作负载需求。本文将对英伟达的四种GPU型号——A100、A800、H100和H800进行深入对比分析&#xff0c;探讨它们在性能、架构、应用场景等方面的差异&#xff0c;以帮助…

LIN 入门(1)

1、概述 LIN 是什么 LIN 是 Local Interconnect Network 的缩写&#xff0c;是基于 UART/SCI(Universal Asynchronous Receiver-Transmitter / Serial Communication Interface&#xff0c;通用异步收发器/串行通信接口)的低成本串行通信协议。可用于汽车、家电、办 公设备等…

代码随想录-二叉树 | 111 二叉树的最小深度

代码随想录-二叉树 | 111 二叉树的最小深度 LeetCode 111 二叉树的最小深度解题思路代码难点总结 LeetCode 111 二叉树的最小深度 题目链接 代码随想录 题目描述 给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说…

讯飞星火模型-语音转文字实现

目录 项目结构 准备音频 接口Demo 准备代码&#xff08;完整修改后&#xff09; 测试提取中文文字代码 结果 下载链接&#xff1a; 这是上周打算试试&#xff0c;提取视频文字之后&#xff0c;制作视频字幕&#xff0c;从而想用大模型来实现&#xff0c;基本的demo可以在…

图像的混合与渐进变换

1.实验目的 平常我们看到的美图秀秀等两个图片混合是怎么生成的呢&#xff0c;今天我们看看图像处理关于这部分怎么做的&#xff1f; 2.实验条件 pycharm python编译器 3.实验代码 # File: 图像混合与渐进变换.py # Author: chen_song # Time: 2024/6/11 下午6:08 "…

CorelDRAW® Graphics Suite 全新 2024 专业图形设计软件

CorelDRAW Graphics Suite 是配备齐全的专业设计工具包&#xff0c;可以非常高的效率提供令人惊艳的矢量插图、布局、照片编辑和排版项目。价格实惠的订阅获得令人难以置信的持续价值&#xff0c;即时、有保障地获得独家的新功能和内容、一流的性能&#xff0c;以及对最新技术的…

【C语音 || 数据结构】二叉树--堆

文章目录 前言堆1.1 二叉树的概念1.2 满二叉树和完美二叉树1.3 堆的概念1.4 堆的性质1.4 堆的实现1.4.1堆的向上调整算法1.4.1堆的向下调整算法1.4.1堆的接口实现1.4.1.1堆的初始化1.4.1.2堆的销毁1.4.1.3堆的插入1.4.1.4堆的删除1.4.1.4堆的判空1.4.1.4 获取堆的数据个数 前言…

轻松玩转新商业模式:工会排队!

在当今数字化时代&#xff0c;互联网的蓬勃发展不仅重塑了商业模式&#xff0c;也深刻改变了消费者的购物习惯。传统的实体零售店面与在线销售平台正面临着巨大的市场挑战。然而&#xff0c;正是这些变革为品牌提供了新的发展机遇。通过创新的商业模式和有效的私域流量管理&…