文章目录
- 业务处理实现思路
- 业务处理类设计
- 成员变量
- 成员函数
- RunModule
- upLoad
- listShow
- downLoad
- getETagInfo
业务处理实现思路
云备份项目中 ,业务处理模块是针对客户端的业务请求进行处理,并最终给与响应。而整个过程中包含以下要实现的功能:
- 借助网络通信模块httplib库搭建http服务器与客户端进行网络通信
- 针对收到的请求进行对应的业务处理并进行响应(文件上传,列表查看,文件下载(包含断点续传))
业务处理类设计
成员变量
private:
int _server_port;//服务端端口
std::string _server_ip;//服务端ip
std::string _download_prefix;//下载前缀
static std::string _back_dir;//备份文件
httplib::Server _server;//创建server对象,用于搭建服务器
成员函数
private:
static void upLoad(const httplib::Request& rq, const httplib::Response& rp)//上传请求,上传数据到服务器
{}
static std::string timeToString(time_t t)//返回一个年月日格式的字符类型时间
{
return std::ctime(&t);
}
static void listShow(const httplib::Request& rq, httplib::Response& rp)//响应数据到客户端浏览器
{}
static std::string getETagInfo(const BackupInfo& info)//获取ETag信息
{}
static void downLoad(const httplib::Request& rq, httplib::Response& rp)//下载请求,下载数据到客户端
{}
public:
serevr()
{
//初始化一些数据
Config* cnf = Config::getIstance();
_server_port = cnf->getServerPort();
_server_ip = cnf->getServerIp();
_download_prefix = cnf->getDownloadPrefix();
_back_dir = cnf->getBackDir();
}
bool RunModule()//运行模块
{}
RunModule
HTTP文件上传请求格式:
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--
响应:
HTTP/1.1 200 OK
Content-Length: 0
HTTP文件列表获取请求格式:
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>
</html>
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//告诉客户端支持断点续传
HTTP断点续传:
GET /download/a.txt http/1.1
Content-Length: 0
If-Range: "文件唯一标识"
Range: bytes=89-999
响应:
HTTP/1.1 206 Partial Content
Content-Length:
Content-Range: bytes 89-999/100000
Content-Type: application/octet-stream
ETag: "inode-size-mtime一个能够唯一标识文件的数据"
Accept-Ranges: bytes
对应文件从89到999字节的数据
bool RunModule()
{
_server.Post("/upload",upLoad);//处理上传文件请求
_server.Get("/listshow", listShow);//处理展示页面请求
_server.Get("/", listShow);//处理展示页面请求
std::string url = _download_prefix + "(.*)";
_server.Get(url,downLoad);//处理下载请求
_server.listen("0.0.0.0", _server_port);//监听所有
//上面Post/Get的第二个参数全是回调函数, 业务处理逻辑将在其中进行
return true;
}
upLoad
static void upLoad(const httplib::Request& rq, const httplib::Response& rp)
{
//1.判断是否有"file"字段, 这个字段是我们自己设置的
bool ret = rq.has_file("file");
if(ret == false)
{
return ;
}
//2.获取file字段的所有内容
const auto& file = rq.get_file_value("file");
std::string real_path = _back_dir + fileUtil(file.filename).fileName();
//3.将file字段里的content内容写入到real_path文件中
fileUtil fu(real_path);
fu.setContent(file.content);
//4.创建文件信息,并管理起来
BackupInfo info;
info.NewBackupInfo(real_path);
_data->insert(info);
return;
}
listShow
返回一个HTML页面
效果如图所示
static void listShow(const httplib::Request& rq, httplib::Response& rp)
{
//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& e : arry)
{
ss << "<tr>";
std::string filename = fileUtil(e.real_path).fileName();
ss << "<td><a href='" << e.url << "'>" << filename << "</a></td>";
ss << "<td align='right'>";
ss << timeToString(e.modify_time);
ss << "</td>";
ss << "<td align='right'>";
ss << e.file_size / 1024 << "K";
ss << "</td>";
ss << "</tr>";
}
ss << "</table></body></html>";
rp.body = ss.str();
rp.set_header("Content-Type", "text/html");
rp.status = 200;
}
downLoad
static void downLoad(const httplib::Request& rq, httplib::Response& rp)
{
//1.获取url
std::string url = rq.path;
//std::cout << url << std::endl;
//2.通过url获取文件信息
BackupInfo info;
_data->getBifoByUrl(url, &info);
//std::cout << info.real_path << std::endl;
//3.判断是否是压缩文件
if(info.pack_flag == true)
{
//解压文件
fileUtil fu(info.pack_path);
fu.uncompress(info.real_path);
//删除压缩文件, 并修改BackupInfo信息
fu.Remove();
info.pack_flag = false;
_data->insert(info);
}
// if(rq.has_header("If-Range"))
// std::cout << "hello" << std::endl;
// else
// std::cout << "no" << std::endl;
// for(auto& e : rp.headers)
// {
// std::cout << e.second << std::endl;
// }
//4.获取文件内容,并设置响应内容
fileUtil fu(info.real_path);
fu.getContent(&rp.body);
//Accept-Ranges 告诉客户端服务器支持断点续传
rp.set_header("Accept-Ranges", "bytes");
rp.set_header("ETag", getETagInfo(info));
rp.set_header("Content-Type", "application/octet-stream");
//rp.status = 200;
//判断是否有If-Range字段,并且If-Range字段的ETag内容和getETagInfo(info)的ETag内容一样
if(rq.has_header("If-Range") && rq.get_header_value("If-Range") == getETagInfo(info))
{
//206 断点续传
rp.status = 206;
//std::cout << rp.status << std::endl;
}
else
{
//200响应全文
rp.status = 200;
}
}
getETagInfo
//服务器自己设计的唯一标识格式
static std::string getETagInfo(const BackupInfo& info)
{
std::string etag;
etag += fileUtil(info.real_path).fileName();
etag += "-";
etag += std::to_string(info.file_size);
etag += "-";
etag += std::to_string(info.modify_time);
return etag;
}