守望相助

一、项目简介:        

       最近学完了Linux操作系统部分的知识,想做个项目整合一下所学的知识,发现自己平时学习过程中在电脑上保存了很多文件,但是电脑平时又不方便携带,想着能不能将文件保存在云端,自己不再电脑旁边的时候能够通过手机浏览器获取文件。说干就干吧。首先放出我的项目设计图。

        项目的总体设计是:云服务器上的服务端用来将客户端上传的文件进行压缩存储,待用户使用的时候通过浏览器提供下载服务,本地的客户端进行指定目录的监控,将该目录中的非热点文件进行上传。

二、解决方案

1、判断文件是否是热点文件?

     在Linux下使用了内核中的stat结构体,这个结构体中保存了文件的最后一次修改时间,如果当前时间减去最后一次修改时间>设定的阈值(10s),表明这时一个非热点文件

     在Windows下,通过文件的etag信息,得到文件的大小和最后一次修改时间,组织成string,与初始化阶段保存的etag信息进行对比,如果两者不同,认为这时一个非热点文件。

2、HTTP服务器是怎么搭建的?

     在项目中使用了GitHub上的开源httplib库,直接使用库中提供的现成的接口,http服务器主要提供的功能有:响应文件列表请求、响应文件上传请求、响应文件下载请求。

     在httplib开源库中、提供了Server和Client类,Server类中针对HTTP的请求类型进行了封装,比如客户端的文件上传请求PUT,封装的重载函数如下:

std::shared_ptr<Response> Put(const char *path, const std::string &body,
                                const char *content_type);

  std::shared_ptr<Response> Put(const char *path, const Headers &headers,
                                const std::string &body,
                                const char *content_type);

  std::shared_ptr<Response> Put(const char *path, size_t content_length,
                                ContentProvider content_provider,
                                const char *content_type);

很容易看懂,传入请求的资源路径,正文数据,和正文数据类型,它就能给指定的服务端发送数据了。

接下来看服务端:它要能区别出不同的客户端请求,做出对应的回应:

 Server &Get(const char *pattern, Handler handler);
  Server &Post(const char *pattern, Handler handler);
  Server &Post(const char *pattern, HandlerWithContentReader handler);
  Server &Put(const char *pattern, Handler handler);
  Server &Put(const char *pattern, HandlerWithContentReader handler);
  Server &Patch(const char *pattern, Handler handler);
  Server &Patch(const char *pattern, HandlerWithContentReader handler);
  Server &Delete(const char *pattern, Handler handler);
  Server &Options(const char *pattern, Handler handler);

可以看到对应客户端的不同请求,Server类提供了一系列的响应函数,第一个参数是请求的资源路径,第二个参数是对应的回调函数,出来客户端的请求。我是这样使用这些函数的:

可以看到浏览器访问主机的IP和端口后面附加请求资源路径,服务端就能调用程序员开发的回调函数处理请求。

class Server
    {
        public:
            bool Start()
            {
                _server.Put("/(.*)",Upload);
                _server.Get("/list",List);
                _server.Get("/download/(.*)",Download);
                //(.*)正则表达式匹配/download/后的任意字符,为了避免有文件名字叫list与上边list请求混淆
                //在路由表中添加了三个请求回调函数,针对什么样的请求回调什么样的方法
               _server.listen("0.0.0.0",9000);
                return true;

            }//启动网络通信模块
        private:
            //文件上传请求 
            static void Upload(const httplib::Request & req,httplib::Response &rsp)
            {
               /* rsp.status=200;
                rsp.set_content("upload",6,"text/html");
            */
                std::string filename=req.matches[1];
                std::string pathname=BACKUP_DIR+filename;
                FileUtil::Write(pathname,req.body);//向备份文件夹中写入数据,文件不存在则创建
                data_manage.Insert(filename,filename);
                rsp.status=200;
                return;
            }

            //文件列表请求
            static void List(const httplib::Request & req,httplib::Response &rsp)
            {
              /*  rsp.status=200;
               // set_content(正文数据,正文数据长度,正文类型-Content-Type)
               // rsp.body=upload;
               //rsp.set_header("content-Type","text/html");
               
               rsp.set_content("list",4,"text/html");
                */
                std::vector<std::string> list;
                data_manage.GetAllName(&list);
                std::stringstream tmp;
                tmp<<"<html><body><hr />";
                for(int i=0;i<list.size();++i)
                {
                    tmp<<"<a href='/download/"<<list[i]<<"'>"<<list[i]<<"</a>";
                    //tmp<<"<a href='/download/a.txt'>"<<"a.txt"<< "</a>"
					//组织成超链接,在点击文件名的时候给服务器发送
                    tmp<<"<hr />";
                }
                tmp<<"<hr /></body></html>";
                 rsp.set_content(tmp.str().c_str(),tmp.str().size(),"text/html");
                 rsp.status=200;
                return ;

            }
            //文件下载处理回调函数
            static void Download(const httplib::Request & req,httplib::Response &rsp)
            {
                std::string filename =req.matches[1];
                if(data_manage.Exists(filename)==false)
                {
                    rsp.status=404;
                    return ;
                }
                std::string pathname =BACKUP_DIR+filename;
                if(data_manage.IsCompress(filename)==true)
                {

                    std::string gzfile;
                    data_manage.GETGzname(filename,&gzfile);
                    std::string gzpathname=GZFILE_DIR+gzfile;
                    
                    CompressUtil::UnCompress(gzpathname,pathname);
                    data_manage.Insert(filename,filename);
                    unlink(gzpathname.c_str());

                }
                
                    FileUtil::Read(pathname,&rsp.body);
                    rsp.set_header("Content-Type","application/octet-stream");//二进制流文件下载
                    rsp.status=200;
                    return ;
            }
        private:
            std::string _file_dir;//文件上传备份路径
            httplib::Server _server;


    };

说明:download请求是浏览器上的超链接触发的,能够直接在浏览器中下载,回调函数都是只有两个参数,所以定义成静态函数,没有隐含的this指针。

3、服务端为什么使用读写锁

     因为多个执行流要针对文件管理模块中的临界资源hash对象,进行读写操作,读写锁的特点是读共享写互斥,可以提高程序响应速度,文件管理模块的hash对象操作逻辑也是如此。

4、文件管理模块的工作流程是怎样的的?

     服务端的文件管理模块使用了一个hash对象,通过pair的first和second标记文件的状态,在服务端,初始化加载的时候,对pair中的两个元素放成相同的文件名,表示没有被压缩,当文件被认定为非热点被压缩后,改变对应pair中的second参数,成为压缩包名,这时就认为文件已经压缩存储了。

    客户端的文件管理模块也是同样的原理,只是pair的second保存成文件的etag信息。

三、项目地址:https://github.com/lixuhui123/cloud_backup

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值