• C++项目——云备份-⑦-服务端业务处理模块设计与实现


    专栏导读

    🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。

    🌸专栏简介:本文收录于 C++项目——云备份

    🌸相关专栏推荐:C语言初阶系列C语言进阶系列 C++系列数据结构与算法Linux
    🌸项目Gitee链接:https://gitee.com/li-yuanjiu/cloud-backup

    在这里插入图片描述

    1.业务处理实现思路

    服务端业务处理模块中,将业务处理模块与网络通信模块进行了合并,因为网络通信模块httplib库已经帮我们完成。

    • 搭建网络通信服务器:借助httplib库完成
    • 业务处理请求:
      • 文件上传请求:客户端上传需要备份的文件——服务端响应上传文件成功;
      • 文件列表请求:客户端浏览器请求一个备份文件的展示页面——服务端响应该页面;
      • 文件下载请求:客户端通过展示页面,点击下载文件——服务端响应客户端要下载的文件数据。

    2.网络通信接口设计

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

    2.1文件上传请求

    客户端文件上传请求

    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--
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    当服务器收到POST方法的/upload请求,我们则认为这是一个文件上传请求,解析请求后(由httplib完成),得到文件数据,将数据写入文件中(创建对应的备份文件)。

    服务端响应

    HTTP/1.1 200 OK
    Content-Length: 0
    
    • 1
    • 2

    2.2文件列表获取请求

    客户端文件列表查看请求

    GET /list HTTP/1.1
    Content-Length: 0
    
    • 1
    • 2

    服务端响应

    HTTP/1.1 200 OK
    Content-Length:
    Content-Type: text/html
    <html>
    	<head>
    	 	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    		<title>Page of Downloadtitle>
    	head>
    	<body>
    		<h1>Downloadh1>
    		<table>
    			<tr>
    				 <td><a href="/download/a.txt"> a.txt a>td>
    				 <td align="right"> 1994-07-08 03:00 td>
    				 <td align="right"> 27K td>
    			tr>
    		table>
    	body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.3文件下载请求

    客户端文件下载请求

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

    服务端响应

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

    3.业务处理类设计

    Service类主要包含以下成员:

        class Service
        {
        public:
            Service();
            bool RunModule(); // 主逻辑执行函数
        private:
       		// 文件上传请求处理函数
            static void Upload(const httplib::Request &req, httplib::Response &rsp); 
            // 日期格式化函数
            static std::string TimetoStr(time_t t);
            // 展示页面请求处理函数
            static void ListShow(const httplib::Request &req, httplib::Response &rsp);
            // 获取Etag函数
            static std::string GetETag(const BackupInfo &info);
            // 文件下载请求处理函数
            static void Download(const httplib::Request &req, httplib::Response &rsp);
        private:
            int _server_port; // 服务器端口
            std::string _server_ip; // 服务器IP
            std::string _download_prefix; // 文件下载请求前缀
            httplib::Server _server; // Server类对象用于搭建服务器
        };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.业务处理类实现与整理

    #ifndef __MY_SERVICE__
    #define __MY_SERVICE__
    #include "util.hpp"
    #include "config.hpp"
    #include "data.hpp"
    #include "httplib.h"
    
    extern cloud::DataManager* _data;
    namespace cloud
    {
        class Service
        {
        public:
            Service()
            {
                Config* config = Config::GetInstance();
                _server_port = config->GetServerPort();
                _server_ip = config->GetSeverIp();
                _download_prefix = config->GetDownloadPrefix();
            }
            bool RunModule()
            {
                _server.Post("/upload", Upload);
                _server.Get("/listshow", ListShow);
                _server.Get("/", ListShow);
                _server.Get("/download/(.*)", Download);
                _server.listen(_server_ip.c_str(), _server_port);
                return true; 
            }
        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");
                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 std::string TimetoStr(time_t t)
            {
                std::string tmp = std::ctime(&t);
                return tmp;
            }
            static void ListShow(const httplib::Request &req, httplib::Response &rsp)
            {
                // 1.获取所有文件的备份信息
                std::vector<BackupInfo> array;
                _data->GetAll(&array);
                // 2.根据所有备份信息,组织html文件数据
                std::stringstream ss;
                ss << "Download";
                ss << "

    Download

    ";for(auto&a : array){ ss <<""; std::string filename =FileUtil(a.real_path).FileName(); ss <<""; ss <<""; ss <<"";} ss <<"
    " << filename << " " << TimetoStr(a.mtime) << " " << a.fsize / 1024 << "k
    "
    ; rsp.body = ss.str(); rsp.set_header("Content-Type", "text/html"); rsp.status = 200; } static std::string GetETag(const BackupInfo &info) { // etag: filename-fsize-mtime FileUtil fu(info.real_path); std::string etag = fu.FileName(); etag += '-'; etag += std::to_string(info.fsize); etag += '-'; etag += std::to_string(info.mtime); return etag; } static void Download(const httplib::Request &req, httplib::Response &rsp) { // 1.获取客户端请求的路径资源,如果被压缩,要先解压缩 // 2.根据资源路径,获取文件备份信息 BackupInfo info; _data->GetOneByURL(req.path, &info); // 3.判断文件是否被压缩,如果被压缩,要先解压缩 if(info.pack_flag == true) { FileUtil fu(info.pack_path); fu.UnCompress(info.real_path); // 将文件解压到备份目录下 // 4.删除压缩包,修改备份信息(已经没有被压缩) fu.Remove(); info.pack_flag = false; _data->Updata(info); } 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-Range字段则是正常下载,或者如果有这个字段,但是 // 它的值与当前文件的etag不一致,则必须重新返回全部数据 // 5.读取文件数据,放入rsp.body中 FileUtil fu(info.real_path); if(retrans == false) { fu.GetContent(&rsp.body); // 6.设置相应头部字段:Etag, 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 { // httplib内部实现了对于区间请求也就是断点续传请求的处理 // 只需要我们用户将文件所有数据读取到rsp.body中,它内部会自动根据请求区间 // 从body中取出指定区间数据进行响应 // std::string range = req.get_header_value("Range"); bytes=starts-end fu.GetContent(&rsp.body); fu.GetContent(&rsp.body); rsp.set_header("Accept-Ranges", "bytes"); rsp.set_header("ETag", GetETag(info)); // rsq.set_header("Content-Range", "bytes start-end/fsize"); rsp.status = 206; } } private: int _server_port; std::string _server_ip; std::string _download_prefix; httplib::Server _server; }; } #endif
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148

    在这里插入图片描述

  • 相关阅读:
    Docker第一天作业
    CLS-PEG-DBCO,胆固醇-聚乙二醇-二苯基环辛炔,可用于改善循环时间
    vue-manage-system升级到vue3的开发总结
    要如何才能抑制局部放电试验干扰?
    Qt应用开发(基础篇)——列表视图 QListView
    IntelliJ IDEA、.NET 工具变贵,JetBrains 宣布全家桶涨价!
    数据归一化的常见方法
    解析csv文件 流数据问题
    Linux命令使用案例
    您的企业内容管理(ECM)系统对敏感信息的保护程度如何?
  • 原文地址:https://blog.csdn.net/gllll_yu/article/details/134082965