1. 业务处理

作用

业务处理模块是对客户端业务请求进行处理

功能

1.文件上传请求备份客户端上传的文件 响应上传成功
2.文件列表请求客户端浏览请求备份一个备份文件展示页面响应页面
3.文件下载请求通过展示页面 点击下载 响应客户端下载的文件数

2. 代码框架编写

_server_port ——服务端端口号
_server_ip ——服务ip地址
_download_prefix ——下载请求前缀


因为业务处理模块 也会访问数据管理
所以在service.hpp中 使用 extern 修饰cloud命名空间中的DataManger类中的 全局变量 _data可以在其他.c 或者.cpp中使用


构造函数

调用 Config类中的 GetInstance 函数 创建对象 config
再调用 config类中的 GetServerPort 函数 获取客户端端口号
调用GetServerIp 函数 获取客户端IP地址
调用 GetDownloadPrefix 函数 获取URL前缀路径


Post请求方法upload资源 表示文件上传
Get请求方法listshow资源 表示文件列表请求
若只输入 / 表示什么没有 没有任何资源路径请求


Get请求方法download资源表示文件下载
使用正则表达式
.表示匹配nr之外的任何单个字符
*表示 匹配前面的子表达式任意
. *表示匹配任意个字符 任意次


UpLoad ——文件上传请求

首先判断没有上传的文件区域
没有上传的文件区域,就将其状态设置为400 并返回


若有上传文件区域 ,则获取文件包含的各项数据
通过Config类中的getinstance函数 创建对象,再通过对象去调用 GetBackDir函数 获取备份文件存放目录


realpath表示实际路径备份文件目录 加上 文件名组成
但是file文件中的filename 是包含路径的 ,所以通过调用FileUtil类的匿名对象 去调用FileName函数 获取文件名称即可


通过realpath 实例化一个 FileUtil类的 fu对象
调用Secontent函数realpath数据写入文件中

通过Cloud类中的BackupInfo实例化一个对象 info
调用 NewBackupInfo函数 获取各项属性信息info
再通过 DataManger类中的 Insert函数 将info中的信息 添加到 _table哈希表中



ListShow —— 展示页面请求处理实现

定义一个 BackupInfo类型数组 arry
调用DataManger类中的 GetAll 函数 将table哈希表中数据 全部放入 arry数组


当前html格式备份文件数据放入其中


定义一个 stringstream流 类型变量 ss
html格式的前两行数据先进行导入到ss中


由于中间的数据是每一个备份文件信息 所以需要遍历arry数组

URL链接 文件名 应该随着每一个文件的不同而改变
所以就不能整体复制过来,需要分别复制

由于URL链接 是由一个双引号扩起来的,与字符串双引号会造成冲突
html中不区分双引号单引号 因此改为单引号
文件名称通过文件实际存储路径 real_path 实例化一个匿名对象 来调用FileName 获取文件名


存储的是文件的最后一次修改时间,正常来说 只需用 mtime 即可
但是mtime是一个时间所以调用接口 ctime时间戳转化为时间格式的字符串


调用 fsize函数 获取 文件大小字节单位
html中 是按照KB表示的,而1KB等于1024字节
所以使其除以1024 获取对应的KB个数



Download —— 下载请求的处理实现

http中的 ETag 字段 存储一个资源唯一标识
客户端第一次下载文件时,会受到这个响应信息
第二次下载 就会将这个信息发送给服务器 想要让服务器根据唯一标识 判断这个资源有没有修改
如果没有修改直接使用原先缓存的数据 不用重新下载了


http协议本身对与ETag中是什么数据并不关心 只要服务能够自己处理即可
ETag文件名 – 文件大小最后一次修改时间 组成


设置 GetETag 函数 获取http中的 ETage字段


table哈希表中寻找 req.path ,若找到req.path返回 对应value 值 并传给 info


BackupInfo 类的 pack_flag 变量 表示压缩标志
pack_flagtrue 则表示 被压缩进入判断

pack_path 表示 压缩包路径
使用pack_path 实例化一个 FileUtil类的对象 fu

real_path 表示 文件实际存储路径
调用 UnCompress 函数 将 pack_path 解压缩 并将其中的数据 放入 real_path 备份目录

pack_flag 改为 false 并使用 update函数 向 table 哈希表中 对应的 path更新对应的keyinfo


通过 real_path 实例化一个 FileUtil类的对象 fu
调用 GetContent 函数 将 real_path中的数据 放入 body


断点续传实现

功能 当文件下载过程中 因为某种异常中断 如果再次进行从头下载 效率比较
因为需要将之前已经传输过的数据 再次传输一遍
因此断点续传 就是从上次下载断开位置 重新下载即可 之前已经传输过的数据 将不需要重新传输

目的:提高文件的重新传输效率

实现思想客户端在下载文件的时候 要每次接收到数据 写入文件后记录自己当前下载的数据量
异常下载中断时,下次断点续传时候 将要重新下载的数据区间(下载的起始位置结束位置)
发送给服务服务收到后 仅仅回传 客户需要区间数据即可

需要考虑问题
上传下载文件之后 这个文件在服务器上被修改了 则这时候将不能重新断点续传
而是应该重新进行文件下载操作


http协议中断点续传的实现:
告诉服务器下载区间范围
服务器上要检测一次下载之后 这个文件是否修改

206状态 状态描述为 Partial Content
表示 部分内容 服务器成功处理部分GET请求
处理区间范围内的请求


上述 Download数中 进行修改

retrans表示断点续传 默认设置false
定义一个string类型字符串 old_etag 将If-Range字段对应的value值 传给 old_etag
调用 GetETag函数 获取 Etag字段
将 if_range字段value值 与 请求文件最新etag比较 ,若一致 则说明为断点续传


httplib 内部 对 Range 请求 已经 做出了 额外操作
外部用户只需把文件的所有数据 读取responsebody中即可
自己就会根据 所请求的区间范围 进行处理body截取指定范围的数据 进行客户端的响应


service .hpp


#ifndef  _MY_SERVICE_//避免头文件重复包含
#define  _MY_SERVICE_
#include "data.hpp"
#include "httplib.h"

extern cloud::DataManager *_data;
namespace cloud
{
   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)// 获取展示页面处理
     {
         //获取所有文件信息
          std::vector<BackupInfo> arry;
          _data->GetAll(&amp;arry);
          
         //根据所有备份信息 组织html文件数据
          std::stringstream ss;
          ss << "<html><head><title>Download</title><meta charset='UTF-8'></head>";
          ss<<"<body><hl>Download</hl><table>";
          for(auto &amp;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;//状态设置为200 表示成功响应
          return ;
     }
    
     
    
    static std::string GetETag(const  BackupInfo &amp; info)//获取 ETag字段
    {
         //  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)//文件下载请求
     {
         // 获取客户端请求的资源路径 req.path
          
         // 根据资源路径  获取文件备份信息
         BackupInfo info;
         _data->GetOneByURL(req.path,&amp;info);//通过URL获取单个数
      
         //判断文件是否压缩  如果被压缩 需要解压缩
         if(info.pack_flag==true)//说明压缩
         {
           FileUtil fu(info.pack_path); //使用压缩包路径实例化一个对象
           fu.UnCompress(info.real_path);//解压缩

            // 删除压缩包  修改备份信息
            fu.Remove();//删除文件
            info.pack_flag=false;  
            _data->Update(info);//更新
         }

          // 读取文件数据 放入 rsp.body
          FileUtil fu(info.real_path);
          fu.GetContent(&amp;rsp.body);//将 real_path中的数据 放入 body中
         
         bool retrans =false;//retrans表示断点续传
         std::string old_etag;
         if(req.has_header("If-Range"))//若存在头部字段 则说明为断点续传
         {
             old_etag =req.get_header_value("If-Range");//将头部字段对应的value值传给 old_etag
             if(old_etag ==GetETag(info))//若If-Range字段 的值 与请求文件的最新etag一致 则符合断点续传
             {
                retrans =true;
             }
         }

         //若没有 If-Range 字段 则为正常下载
         //如果有这个字段 但是它的值与当前文件的 etag 不一致 则必须返回全部数据
          if(retrans==false)
          {
            //正常下载
            fu.GetContent(&amp;rsp.body);//将 real_path中的数据 放入 body中
            //设置响应头部字段  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中取出指定区间 数据 进行响应
            fu.GetContent(&amp;rsp.body);//将 real_path中的数据 放入 body中
            rsp.set_header("Accept-Ranges","bytes");//建立头部字段
            rsp.set_header("ETag",GetETag(info));
            rsp.set_header("Content-Type","application/octet-stream");
            rsp.status=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;
     } 
   };
}

#endif 



原文地址:https://blog.csdn.net/qq_62939852/article/details/134743458

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_46670.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注