C++ project – cloud backup-⑦-Design and implementation of server-side business processing module

Article directory

  • Column introduction
  • 1.Business processing implementation ideas
  • 2. Network communication interface design
    • 2.1 File upload request
    • 2.2 File list acquisition request
    • 2.3 File download request
  • 3. Business processing design
  • 4. Business processing class implementation and organization

Introduction to the column

About the author: Hua Xiangyun, undergraduate student One, a rising star creator in the C/C++ field, a mentor in the Rising Star Program, an expert blogger at Alibaba Cloud, and a CSDN content partner… dedicated to learning C/C++ and Linux.

Introduction to the column: This article is included in the C + + project – cloud backup

Related column recommendations: C Language Beginner Series, C Language Advanced Series, C++ Series, Data Structures and Algorithms, Linux
Project Gitee link: https://gitee.com/li-yuanjiu/cloud-backup

1. Business processing implementation ideas

In the server-side business processing module, the business processing module and the network communication module are merged, because the network communication module httplib library has already been completed for us.

  • Build a network communication server: Use the httplib library to complete;
  • Business processing request:
    • File upload request: The client uploads the files that need to be backed up – the server responds that the uploaded file is successful;
    • File list request: The client browser requests a backup file display page – the server responds to the page;
    • File download request: The client clicks to download the file through the display page – the server responds with the file data that the client wants to download.

2. Network communication interface design

The business processing module needs to process the client's request, so we need to define the communication between the client and the server in advance, clarify what kind of request the client sends, and what kind of response the server should give after processing. This is the design of the network communication interface.

2.1 File upload request

Client file upload request

POST /upload HTTP/1.1
Content-Length:11
Content-Type: multipart/form-data;boundary= ----WebKitFormBoundary + 16 bytes random characters
------WebKitFormBoundary
Content-Disposition: form-data;filename="a.txt";
hello world
------WebKitFormBoundary--

When the server receives the /upload request of the POST method, we think it is a file upload request, and after parsing the request (completed by httplib) , obtain the file data, and write the data into the file (create the corresponding backup file).

Server response

HTTP/1.1 200 OK
Content-Length: 0

2.2 File list acquisition request

Client file list view request

GET /list HTTP/1.1
Content-Length: 0

Server response

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 Download</title>
</head>
<body>
<h1>Download</h1>
<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 File download request

Client file download request

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

Server response

HTTP/1.1 200 OK
Content-Length: 100000
ETags: "filename-size-mtime is a data that uniquely identifies the file"
Accept-Ranges: bytes
file data

3. Business processing design

The Service class mainly contains the following members:

 class Service
    {<!-- -->
    public:
        Service();
        bool RunModule(); // Main logic execution function
    private:
   //File upload request processing function
        static void Upload(const httplib::Request & amp;req, httplib::Response & amp;rsp);
        //Date formatting function
        static std::string TimetoStr(time_t t);
        // Display page request processing function
        static void ListShow(const httplib::Request & amp;req, httplib::Response & amp;rsp);
        // Get Etag function
        static std::string GetETag(const BackupInfo & amp;info);
        //File download request processing function
        static void Download(const httplib::Request & amp;req, httplib::Response & amp;rsp);
    private:
        int _server_port; // server port
        std::string _server_ip; // Server IP
        std::string _download_prefix; // File download request prefix
        httplib::Server _server; // Server class object is used to build the server
    };

4. Business processing class implementation and organization

#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 & amp;req, httplib::Response & amp;rsp)
        {<!-- -->
            auto ret = req.has_file("file");
            if(ret == false)
            {<!-- -->
                rsp.status = 400;
                return;
            }
            const auto & amp; 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); // Write data to the file
            BackupInfo info;
            info.NewBackupInfo(realpath); // Organize backup file information
            _data->Insert(info); // Add backup file information to the data management module
            return;
        }
        static std::string TimetoStr(time_t t)
        {<!-- -->
            std::string tmp = std::ctime( & amp;t);
            return tmp;
        }
        static void ListShow(const httplib::Request & amp;req, httplib::Response & amp;rsp)
        {<!-- -->
            // 1. Get the backup information of all files
            std::vector<BackupInfo> array;
            _data->GetAll( & amp;array);
            // 2. Organize html file data based on all backup information
            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 << "</table></body></html>";
            rsp.body = ss.str();
            rsp.set_header("Content-Type", "text/html");
            rsp.status = 200;
        }
        static std::string GetETag(const BackupInfo & amp;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 & amp;req, httplib::Response & amp;rsp)
        {<!-- -->
            // 1. Get the path resource requested by the client. If it is compressed, decompress it first.
            // 2. Obtain file backup information based on the resource path
            BackupInfo info;
            _data->GetOneByURL(req.path, & amp;info);
            // 3. Determine whether the file is compressed. If it is compressed, decompress it first.
            if(info.pack_flag == true)
            {<!-- -->
                FileUtil fu(info.pack_path);
                fu.UnCompress(info.real_path); // Unzip the file to the backup directory
                // 4. Delete the compressed package and modify the backup information (no longer compressed)
                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 there is an If-Range field and the value of this field is consistent with the latest etag of the requested file, it is consistent with breakpoint resumption.
                if(old_etag == GetETag(info))
                {<!-- -->
                    retrans = true;
                }
            }
            // If there is no If-Range field, it is downloaded normally, or if there is this field, but
            // Its value is inconsistent with the etag of the current file, so all data must be returned
            // 5. Read the file data and put it into rsp.body
            FileUtil fu(info.real_path);
            if(retrans == false)
            {<!-- -->
                fu.GetContent( & amp;rsp.body);
                // 6. Set the corresponding header fields: 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 internally implements the processing of interval requests, that is, breakpoint resume requests.
                // We only need our users to read all the data of the file into rsp.body, which will automatically adjust the request range internally.
                // Get the specified interval data from the body to respond
                // std::string range = req.get_header_value("Range"); bytes=starts-end
                fu.GetContent( & amp;rsp.body);
                fu.GetContent( & amp;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