云备份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-mtimeFileUtil 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: bytesrsp.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-endfu.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;}};