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

文章目录

  • 专栏导读
  • 1.业务处理实现思路
  • 2.网络通信接口设计
    • 2.1文件上传请求
    • 2.2文件列表获取请求
    • 2.3文件下载请求
  • 3.业务处理类设计
  • 4.业务处理类实现与整理

专栏导读

作者简介:花想云 ,在读本科生一枚,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--

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

服务端响应

HTTP/1.1 200 OK
Content-Length: 0

2.2文件列表获取请求

客户端文件列表查看请求

GET /list HTTP/1.1
Content-Length: 0

服务端响应

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>

2.3文件下载请求

客户端文件下载请求

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

服务端响应

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

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类对象用于搭建服务器
    };

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

在这里插入图片描述

你可能感兴趣的:(C++项目——云备份,c++,c++项目,云备份,c++项目实战)