云备份day6
- 业务处理
业务处理
云备份项目中 ,业务处理模块是针对客户端的业务请求进行处理,并最终给与响应。而整个过程中包含以下要实现的功能:
- 借助网络通信模块httplib库搭建http服务器与客户端进行网络通信
- 针对收到的请求进行对应的业务处理并进行响应(文件上传,列表查看,文件下载(包含断点续传))
业务处理模块要对客户端的请求进行处理,那么我们就需要提前定义好客户端与服务端的通信,明确客户端发送什么样的请求,服务端处理后应该给与什么样的响应,而这就是网络通信接口的设计。
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; } };