【lesson7】服务端业务处理模块实现

业务处理实现思路

云备份项目中 ,业务处理模块是针对客户端的业务请求进行处理,并最终给与响应。而整个过程中包含以下要实现的功能:

  • 借助网络通信模块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
对应文件从89999字节的数据
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值