【云备份|| 日志 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日志并进行多维度分析。
相关文章
|
9天前
|
监控 Java API
如何将不同业务模块产生的日志 分多文件记录
如何将不同业务模块产生的日志 分多文件记录
10 0
|
1天前
|
Ubuntu Java Linux
查看Linux系统中日志文件
查看Linux系统中日志文件
|
1天前
|
存储 监控 应用服务中间件
查看nginx日志文件
器性能和提高网站可用性。掌握日志文件的路径、查看方法和基本分析技能对于任何服务器管理员来说都是必备技能。
7 1
|
1天前
|
SQL Oracle NoSQL
实时计算 Flink版操作报错合集之报错“找不到对应的归档日志文件”,怎么处理
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
7天前
|
存储 监控 Java
|
12天前
|
存储 安全 Python
[python]使用标准库logging实现多进程安全的日志模块
[python]使用标准库logging实现多进程安全的日志模块
|
6天前
|
应用服务中间件 Linux nginx
Nginx log 日志文件较大,按日期生成 实现日志的切割
Nginx log 日志文件较大,按日期生成 实现日志的切割
27 0
|
6天前
|
C#
C# 写日志文件
C# 写日志文件
12 0
|
7天前
|
关系型数据库 MySQL Linux
Linux——日志文件按天切割
Linux——日志文件按天切割
24 0
|
12天前
|
JSON 安全 Go
[golang]使用logrus自定义日志模块
[golang]使用logrus自定义日志模块

热门文章

最新文章