云备份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;
}
};