【云备份|| 日志 day6】文件业务处理模块

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【云备份|| 日志 day6】文件业务处理模块

image.png

云备份day6

  • 业务处理

业务处理

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

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

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

HTTP文件上传:

request请求

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--


respons响应

HTTP/1.1 200 OK
Content-Length: 0

HTTP文件列表获取:

request请求

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 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>

HTTP文件下载:

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

服务端业务处理模块实现-业务处理类设计

extern cloud::DataManager *_data;
class Service{
private:
int _server_port;
std::string _server_ip;
std::string _url_prefix;
httplib::Server _srv;
public:
static void Upload(const httplib::Request &req, httplib::Response &rsp);
static void List(const httplib::Request &req, httplib::Response &rsp);
static void Download(const httplib::Request &req,httplib::Response &rsp);
public:
Service();
bool RunModule();
}


代码:

class Service{
    private:
      int _server_port;
      std::string _server_ip;
      std::string _download_prefix;
      httplib::Server _server;
    private:
      static void Upload(const httplib::Request &req, httplib::Response &rsp) {
        // post /upload  文件数据在正文中(正文并不全是文件数据)
        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 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> arry;
        _data->GetAll(&arry);
        //2. 根据所有备份信息,组织html文件数据
        std::stringstream ss;
        ss << "<html><head><title>Download</title></head>";
        ss << "<body><h1>Download</h1><table>";
        for (auto &a : arry){
          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) {
        // etg :  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. 获取客户端请求的资源路径path   req.path
        //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->Update(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;
          }
        }
        //4. 读取文件数据,放入rsp.body中
        FileUtil fu(info.real_path);
        if (retrans == false){
          fu.GetContent(&rsp.body);
          //5. 设置响应头部字段: 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_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-Type", "application/octet-stream");
          //rsp.set_header("Content-Range", "bytes start-end/fsize");
          rsp.status = 206;//区间请求响应的是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.c_str(), _server_port);
        return true;
      }
  };


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
20天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
本文介绍了MySQL InnoDB存储引擎中的数据文件和重做日志文件。数据文件包括`.ibd`和`ibdata`文件,用于存放InnoDB数据和索引。重做日志文件(redo log)确保数据的可靠性和事务的持久性,其大小和路径可由相关参数配置。文章还提供了视频讲解和示例代码。
129 11
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
|
20天前
|
SQL Oracle 关系型数据库
【赵渝强老师】Oracle的控制文件与归档日志文件
本文介绍了Oracle数据库中的控制文件和归档日志文件。控制文件记录了数据库的物理结构信息,如数据库名、数据文件和联机日志文件的位置等。为了保护数据库,通常会进行控制文件的多路复用。归档日志文件是联机重做日志文件的副本,用于记录数据库的变更历史。文章还提供了相关SQL语句,帮助查看和设置数据库的日志模式。
【赵渝强老师】Oracle的控制文件与归档日志文件
|
20天前
|
Oracle 关系型数据库 数据库
【赵渝强老师】Oracle的参数文件与告警日志文件
本文介绍了Oracle数据库的参数文件和告警日志文件。参数文件分为初始化参数文件(PFile)和服务器端参数文件(SPFile),在数据库启动时读取并分配资源。告警日志文件记录了数据库的重要活动、错误和警告信息,帮助诊断问题。文中还提供了相关视频讲解和示例代码。
|
2月前
|
监控 Linux 应用服务中间件
系统监控:使用日志文件 journalctl的使用
本文介绍了如何使用`journalctl`命令来监控和查看Linux系统的日志文件,包括查看特定行数、过滤日志级别、实时跟踪日志、按时间段查询日志以及日志轮换和压缩的配置。
57 2
系统监控:使用日志文件 journalctl的使用
|
2月前
|
SQL 数据库
为什么 SQL 日志文件很大,我应该如何处理?
为什么 SQL 日志文件很大,我应该如何处理?
|
2月前
|
开发工具 git
git显示开发日志+WinSW——将.exe文件注册为服务的一个工具+图床PicGo+kubeconfig 多个集群配置 如何切换
git显示开发日志+WinSW——将.exe文件注册为服务的一个工具+图床PicGo+kubeconfig 多个集群配置 如何切换
39 1
|
2月前
|
存储 监控 固态存储
如何监控和优化 WAL 日志文件的存储空间使用?
如何监控和优化 WAL 日志文件的存储空间使用?
|
2月前
|
SQL 数据库
为什么SQL日志文件很大,该如何处理?
为什么SQL日志文件很大,该如何处理?
|
3月前
|
Shell Python
salt自定义模块内使用日志例子
salt自定义模块内使用日志例子
|
3月前
|
Python
Python如何将日志输入到文件里
Python如何将日志输入到文件里