目录
云备份认识
实现目标
服务端工具类实现-文件实用工具类设计
服务端工具类实现- Json 实用工具类设计
服务端程序负责功能
服务端功能模块划分
系统配置信息
单例文件配置类设计
管理的数据信息
如何管理数据
数据管理类的设计
热点管理实现思路
热点管理类的设计
业务处理实现思路
网络通信接口设计
业务处理类设计
客户端程序负责功能
客户端功能模块划分
数据信息设计
文件操作实用类设计
数据管理类设计
文件备份类设计
项目总结
项目扩展
云备份认识
自动将本地计算机上指定文件夹中需要备份的文件上传备份到服务器中。并且能够随时通过浏览器进行查看并且下
载,其中下载过程支持断点续传功能,而服务器也会对上传文件进行热点管理,将非热点文件进行压缩存储,节省磁
盘空间。
实现目标
这个云备份项目需要我们实现两端程序,其中包括部署在用户机的客户端程序,上传需要备份的文件,以及运行在服
务器上的服务端程序,实现备份文件的存储和管理,两端合作实现总体的自动云备份功能。
服务端工具类实现-文件实用工具类设计
class FileUtil
{
private:
std::string _name;
public:
FileUtil(const std::string &name);
size_t FileSize();
time_t LastATime();
time_t LastMTime();
std::string FileName();
bool GetPosLen(std::string *content, size_t pos, size_t len);
bool GetContent(std::string *content);
bool SetContent(std::strint *content);
bool Compress(const std::string &packname);
bool UnCompress(const std::string &filename);
bool Exists();
bool CreateDirectory();
bool ScanDirectory(std::vector<std::string> *arry);
};
服务端工具类实现- Json 实用工具类设计
class JsonUtil{
public:
static bool Serialize(const Json::Value &root, std::string *str);
static bool UnSerialize(const std::string &str, Json::Value *root);
};
服务端程序负责功能
针对客户端上传的文件进行备份存储
能够对文件进行热点文件管理,对非热点文件进行压缩存储,节省磁盘空间。
支持客户端浏览器查看访问文件列表。
支持客户端浏览器下载文件,并且下载支持断点续传。
服务端功能模块划分
数据管理模块:负责服务器上备份文件的信息管理。
网络通信模块:搭建网络通信服务器,实现与客户端通信。
业务处理模块:针对客户端的各个请求进行对应业务处理并响应结果。
热点管理模块:负责文件的热点判断,以及非热点文件的压缩存储。
系统配置信息
使用文件配置加载一些程序的运行关键信息可以让程序的运行更加灵活。
配置信息:
- 热点判断时间
- 文件下载URL前缀路径
- 压缩包后缀名称
- 上传文件存放路径
- 压缩文件存放路径
- 服务端备份信息存放文件
- 服务器访问 IP 地址
- 服务器访问端口
{
"hot_time" : 30,
"server_port" : 9090,
"server_ip" : "101.201.39.212",
"download_prefix" : "/download/",
"packfile_suffix" : ".lz",
"pack_dir" : "./packdir/",
"back_dir" : "./backdir/",
"backup_file" : "./cloud.dat"
}
单例文件配置类设计
使用单例模式管理系统配置信息,能够让配置信息的管理控制更加统一灵活。
#define CONFIG_FILE "./cloud.conf"
classConfig{
private:
time_t_hot_time;
int_server_port;
std::string_server_ip;
std::string_url_prefix;
std::string_arc_suffix;
std::string_pack_dir;
std::string_back_dir;
std::string_manager_file;//备份信息的配置管理
private:
staticstd::mutex_mutex;
staticConfig*_instance;
Config();
public:
boolReadConfig(conststd::string&filename); intGetHotTime();
intGetServerPort();
std::stringGetServerIp();
std::stringGetURLPrefix();
std::stringGetArcSuffix();
std::stringGetPackDir();
std::stringGetBackDir();
std::stringGetManagerFile(); public:
staticConfig*GetInstance();
};
管理的数据信息
- 文件实际存储路径
- 文件是否压缩标志
- 压缩包存储路径
- 文件访问URL
- 文件最后一次修改时间
- 文件最后一次访问时间
- 文件大小
如何管理数据
内存中以文件访问 URL 为 key ,数据信息结构为 val ,使用哈希表进行管理,查询速度快。使用 url 作为 key是因为往后客户端浏览器下载文件的时候总是以url作为请求
采用文件形式对数据进行持久化存储(序列化方式采用Json格式或者自定义方式)
数据管理类的设计
typedefstructBackupInfo{
intpack_flag;
time_tmtime;
time_tatime;
size_tfsize;
std::stringrealpath;
std::stringurl;
std::stringpackpath;
boolNewBackupInfo(conststd::string&realpath);
}BackupInfo;
classDataManager{
private:
FileUtil_backup_file;
pthread_rwlock_t_rwlock;
std::unordered_map<std::string, BackupInfo>_table; public:
DataManager();
boolInitLoad();//初始化程序运行时从文件读取数据
boolStorage(); //每次有信息改变则需要持久化存储一次
boolInsert(conststd::string&key, constBackupInfo&val);
boolUpdate(conststd::string&key, constBackupInfo&val);
boolGetOneByURL(conststd::string&key, BackupInfo*info);
boolGetOneByRealPath(conststd::string&key, BackupInfo*info); boolGetAll(std::vector<BackupInfo>*arry);
};
热点管理实现思路
服务器端的热点文件管理是对上传的非热点文件进行压缩存储,节省磁盘空间。
而热点文件的判断在于上传的文件的最后一次访问时间是否在热点判断时间之内,比如如果一个文件一天都没有被访问过我们就认为这是一个非热点文件,其实就是当前系统时间,与文件最后一次访问时间之间的时间差是否在一天之内的判断。
而我们需要对上传的文件每隔一段时间进行热点检测,相当于遍历上传文件的存储文件夹,找出所有的文件,然后通过对逐个文件进行时间差的判断,来逐个进行热点处理。
基于这个思想,我们需要将上传的文件存储位置与压缩后压缩文件的存储位置分开。这样在遍历上传文件夹的时候不至于将压缩过的文件又进行非热点处理了。
关键点:
- 上传文件有自己的上传存储位置,非热点文件的压缩存储有自己的存储位置
- 遍历上传存储位置文件夹,获取所有文件信息。
- 获取每个文件最后一次访问时间,进而完成是否热点文件的判断。
- 对非热点文件进行压缩存储,删除原来的未压缩文件。
热点管理类的设计
//因为数据管理是要在多个模块中访问的,因此将其作为全局数据定义,在此处声明使用即可externDataManager*_data;
classHotManager{
private:
std::string_back_dir;
std::string_pack_dir;
std::string_arc_suffix;
std::string_url_prefix;
time_t_hot_time;
public:
HotManager();
boolHotJudge(conststd::string&file); boolRunModule();
};
业务处理实现思路
云备份项目中,业务处理模块是针对客户端的业务请求进行处理,并最终给与响应。而整个过程中包含以下要实现的功能:
- 借助网络通信模块httplib库搭建http服务器与客户端进行网络通信
- 针对收到的请求进行对应的业务处理并进行响应(文件上传,列表查看,文件下载(包含断点续传))
网络通信接口设计
业务处理模块要对客户端的请求进行处理,那么我们就需要提前定义好客户端与服务端的通信,明确客户端发送什么
样的请求,服务端处理后应该给与什么样的响应,而这就是网络通信接口的设计。
HTTP 文件上传:
POST/uploadHTTP/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.1200 OK
Content-Length: 0
HTTP 文件列表获取:
GET/listHTTP/1.1
Content-Length: 0
HTTP/1.1200 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"> 2023-8-13 03:00 </td>
<td align="right"> 27K </td>
</tr>
</table>
</body>
</html>
HTTP 文件下载:
GET/download/a.txthttp/1.1
Content-Length: 0
HTTP/1.1200 OK
Content-Length: 100000
ETags: "filename-size-mtime一个能够唯一标识文件的数据"Accept-Ranges: bytes
HTTP 断点续传:
GET/download/a.txthttp/1.1
Content-Length: 0
If-Range: "文件唯一标识"
Range: bytes=89-999
HTTP/1.1206 Partial Content
Content-Length:
Content-Range: bytes 89-999/100000
Content-Type: application/octet-stream
ETag: "inode-size-mtime一个能够唯一标识文件的数据"Accept-Ranges: bytes
业务处理类设计
//因为业务处理的回调函数没有传入参数的地方,因此无法直接访问外部的数据管理模块数据
//可以使用lamda表达式解决,但是所有的业务功能都要在一个函数内实现,于功能划分上模块不够清晰
//因此将数据管理模块的对象定义为全局数据,在这里声明一下,就可以在任意位置访问了
externcloud::DataManager*_data;
classService{
private:
int_server_port;
std::string_server_ip;
std::string_url_prefix;
httplib::Server_srv;
public:
staticvoidUpload(consthttplib::Request&req, httplib::Response&rsp);
staticvoidList(consthttplib::Request&req, httplib::Response&rsp);
staticvoidDownload(consthttplib::Request&req,httplib::Response&rsp);
public:
Service();
boolRunModule();
}
客户端程序负责功能
能够自动检测客户机指定文件夹中的文件,并判断是否需要备份
将需要备份的文件逐个上传到服务器
客户端功能模块划分
数据管理模块:负责客户端备份的文件信息管理,通过这些数据可以确定一个文件是否需要备份。
文件检测模块:遍历获取指定文件夹中所有文件路径名称。
网络通信模块:搭建网络通信客户端,实现将文件数据备份上传到服务器。
数据信息设计
客户端要实现的功能是对指定文件夹中的文件自动进行备份上传。但是并不是所有的文件每次都需要上传,我们需要
能够判断,哪些文件需要上传,哪些不需要,因此需要将备份的文件信息给管理起来,作为下一次文件是否需要备份
的判断。因此需要被管理的信息包含以下:
- 文件路径名称
- 文件唯一标识:由文件名,最后一次修改时间,文件大小组成的一串信息
文件操作实用类设计
namespacefs=std::experimental::filesystem;
classFileUtil {
private:
std::string_name;
public:
FileUtil(conststd::string&name) :_name(name) {}
size_tFileSize();
time_tLastATime();
time_tLastMTime();
std::stringFileName();
boolGetPosLen(std::string*content, size_tpos, size_tlen); boolGetContent(std::string*content);
boolSetContent(conststd::string&content);
boolExists();
boolCreateDirectory();
boolScanDirectory(std::vector<std::string>*arry);
};
数据管理类设计
classDataManager{
private:
std::unordered_map<std::string, std::string>_table;
std::string_back_file;
public:
DataManager(conststd::stringback_file);
boolInitLoad();//程序运行时加载以前的数据
boolStorage();//持久化存储
boolInsert(conststd::string&key, conststd::string&val);
boolUpdate(conststd::string&key, conststd::string&val);
boolGetOneByKey(conststd::string&key, std::string*val);
};
文件备份类设计
#define SRV_IP "101.201.39.212"
#define SRV_PORT 9000
classBackUp {
private:
DataManager*_data;
std::string_back_dir;
std::string_back_file;
public:
BackUp(conststd::string&back_dir, conststd::string&back_file)
:_back_dir(back_dir)
, _back_file(back_file){}
std::stringGetFileIdentifier(conststd::string&filename);
boolUpload(conststd::string&filename);
boolIsCanBeUpload(conststd::string&filename);
boolRunModule();
};
项目总结
项目名称:云备份系统
项目功能:搭建云备份服务器与客户端,客户端程序运行在客户机上自动将指定目录下的文件备份到服务器,并且能够支持浏览器查看与下载,其中下载支持断点续传功能,并且服务器端对备份的文件进行热点管理,将长时间无访问文件进行压缩存储。
开发环境:centos7.6/vim 、 g++ 、 gdb 、 makefile 以及 windows10/vs2017
技术特点: http 客户端 / 服务器搭建, json 序列化,文件压缩,热点管理,断点续传,线程池,读写锁,单例模式
项目模块:
服务端:
- 数据管理模块:内存中使用hash表存储提高访问效率,持久化使用文件存储管理备份数据
- 业务处理模块:搭建 http 服务器与客户端进行通信处理客户端的上传,下载,查看请求,并支持断点续传
- 热点管理模块:对备份的文件进行热点管理,将长时间无访问文件进行压缩存储,节省磁盘空间。
客户端:
- 数据管理模块:内存中使用hash表存储提高访问效率,持久化使用文件存储管理备份数据
- 文件检索模块:基于 c++17 文件系统库,遍历获取指定文件夹下所有文件。
- 文件备份模块:搭建 http 客户端上传备份文件。
项目扩展
1. 给客户端开发一个好看的界面,让监控目录可以选择
2. 内存中的管理的数据也可以采用热点管理
3. 压缩模块也可以使用线程池实现
4. 实现用户管理,不同的用户分文件夹存储以及查看
5. 实现断点上传
6. 客户端限速,收费则放开