项目日记 -云备份 -服务端数据管理模块
博客主页:【夜泉_ly】
本文专栏:【项目日记-云备份】
欢迎点赞👍收藏⭐关注❤️
代码已上传 gitee
目录
- 文件信息结构体
- init 初始化
- 数据管理类
- loadInfo 加载文件信息
- storage 持久化存储文件信息
- update 更新信息
- insert 插入信息
- getInfoByURL 根据url获取文件信息
- getInfoBybackPath 根据路径获取文件信息
- getFileInfo 获取所有文件信息
- 测试
我在之前的博客中,
已经实现了服务端工具类,
和服务端配置信息模块。
本篇将实现的是服务端数据管理模块。
CloudBackup/src/datamanager.hpp
文件信息结构体
为了管理文件,
这里选择的是用一个结构体将文件信息组织起来:
struct FileInfo
{
FileInfo() {}
FileInfo(const std::string &backPath) { init(backPath); }
void init(const std::string &backPath);
bool _isPacked;
size_t _fsize;
time_t _mtime;
time_t _atime;
std::string _backPath;
std::string _packPath;
std::string _url;
};
_isPacked
,判断文件是否已经被压缩。
_backPath
,这个就是文件备份的路径,因为最常用,所以也用它来初始化。
_packPath
,这个是文件被压缩后的路径。
_url
,这个是客户端传来的请求路径。
init 初始化
void init(const std::string &backPath)
{
FileUtils f(backPath);
if (!f.exists())
{
std::cerr << "FileInfo::init: " << backPath << " not exist\n";
return;
}
_isPacked = false;
_fsize = f.getSize();
_mtime = f.getMTime();
_atime = f.getATime();
_backPath = backPath;
Config *pc = Config::getInstance();
_packPath = pc->getPackDir() + f.getFileName() + pc->getPackfileSuffix();
_url = pc->getDownloadPrefix() + f.getFileName();
}
一定要检查路径是否存在!
不存在的话一定要打印传入的路径!
_packPath
为 压缩文件目录 + 文件名 + 后缀
_url
为 请求下载的前缀 + 文件名
数据管理类
我是这样想的,
文件数据是唯一的吧,
数据管理类又是大家都会频繁调用的吧,
那继续用单例吧。
class DataManager
{
private:
DataManager();
DataManager(const DataManager&) = delete;
DataManager& operator=(const DataManager&) = delete;
void fileInfoToJson(const FileInfo& fileInfo, Json::Value& cur);
void jsonToFileInfo(const Json::Value& root, FileInfo& cur);
public:
static DataManager* getInstance();
~DataManager();
bool loadInfo();
bool storage();
void update(const FileInfo& info);
bool insert(const FileInfo& info);
bool getInfoByURL(const std::string& key, FileInfo* pInfo);
bool getInfoBybackPath(const std::string& path, FileInfo* pInfo);
bool getFileInfo(std::vector<FileInfo>* pArr);
private:
FileUtils _backupFile;
pthread_rwlock_t _rwlock;
std::unordered_map<std::string, FileInfo> _hash;
private:
static DataManager* _instance;
static std::mutex _mutex;
};
std::mutex DataManager::_mutex;
DataManager* DataManager::_instance = nullptr;
先看成员变量:
-
_backupFile
这个是存备份文件信息的。。文件。。的工具类对象。
主要用于bool storage();
,
因为这个函数每次调用都会设置一下backupFile
的内容。 -
_rwlock
用来解决读写者问题
其中update
、insert
函数,
会修改数据,加写锁,
而getInfoByURL
、getInfoBybackPath
、getFileInfo
函数,
只读取数据,加读锁。
然后然后,为了避免误操作,
我搞了两个类:struct Writer { pthread_rwlock_t *_lock; Writer(pthread_rwlock_t *lock) : _lock(lock) { pthread_rwlock_wrlock(_lock); } ~Writer() { pthread_rwlock_unlock(_lock); } }; struct Reader { pthread_rwlock_t *_lock; Reader(pthread_rwlock_t *lock) : _lock(lock) { pthread_rwlock_rdlock(_lock); } ~Reader() { pthread_rwlock_unlock(_lock); } };
这样就能做到简单的
RAII
了。 -
_hash
,哈希表,
k
为string _url
,v
为struct FileInfo
。
这样的管理,
是为了当用户想要下载一个文件时,
我们可以快速的找到对应文件的信息。
剩下两个静态成员变量,
作用和用法与文件配置类的类似,
就不多说了。
接下来看成员函数:
DataManager() : _backupFile(Config::getInstance()->getBackupFile())
{
std::cout << "create DataManager\n";
if (pthread_rwlock_init(&_rwlock, nullptr))
std::cerr << "DataManager: init _rwlock error\n";
std::cout << "DataManager begin loadinfo\n";
loadInfo();
}
DataManager(const DataManager&) = delete;
DataManager& operator=(const DataManager&) = delete;
void fileInfoToJson(const FileInfo& fileInfo, Json::Value& cur)
{
cur["isPacked"] = fileInfo._isPacked;
cur["atime"] = static_cast<Json::Int64>(fileInfo._atime);
cur["mtime"] = static_cast<Json::Int64>(fileInfo._mtime);
cur["fsize"] = static_cast<Json::Int64>(fileInfo._fsize);
cur["packPath"] = fileInfo._packPath;
cur["backPath"] = fileInfo._backPath;
cur["url"] = fileInfo._url;
}
void jsonToFileInfo(const Json::Value& root, FileInfo& cur)
{
cur._isPacked = root["isPacked"].asBool();
cur._atime = root["atime"].asInt64();
cur._mtime = root["mtime"].asInt64();
cur._fsize = root["fsize"].asInt64();
cur._packPath = root["packPath"].asString();
cur._backPath = root["backPath"].asString();
cur._url = root["url"].asString();
}
public:
static DataManager* getInstance()
{
if (_instance == nullptr)
{
_mutex.lock();
if (_instance == nullptr)
_instance = new DataManager();
_mutex.unlock();
}
return _instance;
}
~DataManager()
{
pthread_rwlock_destroy(&_rwlock);
}
上面这几个,没什么需要注意的。
loadInfo 加载文件信息
bool loadInfo()
{
if (!_backupFile.getSize())
return false;
std::string str;
if (!_backupFile.getContent(&str))
return false;
Json::Value root;
if (!JsonUtils::deSerialize(str, &root))
return false;
for (int i = 0; i < root.size(); i++)
{
FileInfo cur;
jsonToFileInfo(root[i], cur);
_hash[cur._url] = cur;
}
return true;
}
前两句检查不写,之后就会喜提报错:
storage 持久化存储文件信息
bool storage()
{
Json::Value root;
for (const auto &[url, fileInfo] : _hash)
{
Json::Value cur;
fileInfoToJson(fileInfo, cur);
root.append(cur);
}
std::string str;
JsonUtils::serialize(root, &str);
return _backupFile.setContent(str);
}
遍历哈希表,
序列化 fileInfo
中的信息,
append
添加进数组(不这样写loadInfo就不能用root[i])
最后转化为字符串存进 _backupFile
。
update 更新信息
insert 插入信息
bool update(const FileInfo &info)
{
Writer w(&_rwlock);
_hash[info._url] = info;
return storage();
}
bool insert(const FileInfo &info)
{
Writer w(&_rwlock);
_hash[info._url] = info;
return storage();
}
Writer w(&_rwlock);
加写锁
getInfoByURL 根据url获取文件信息
bool getInfoByURL(const std::string &key, FileInfo *pInfo)
{
Reader r(&_rwlock);
if (!pInfo)
return false;
if (!_hash.count(key))
return false;
*pInfo = _hash[key];
return true;
}
Reader r(&_rwlock);
加读锁
getInfoBybackPath 根据路径获取文件信息
bool getInfoBybackPath(const std::string &path, FileInfo *pInfo)
{
Reader r(&_rwlock);
if (!pInfo)
return false;
if (!FileUtils(path).exists())
return false;
*pInfo = FileInfo(path);
return true;
}
Reader r(&_rwlock);
加读锁
getFileInfo 获取所有文件信息
bool getFileInfo(std::vector<FileInfo> *pArr)
{
Reader r(&_rwlock);
if (!pArr)
return false;
pArr->clear();
for (const auto &[url, Info] : _hash)
pArr->push_back(Info);
return true;
}
Reader r(&_rwlock);
加读锁
测试
CloudBackup/src/testdatamanager.cpp
#include "datamanager.hpp"
int main(int argc, char* argv[])
{
using namespace Cloud;
if(argc == 3)
{
DataManager::getInstance()->insert(FileInfo(argv[1]));
DataManager::getInstance()->insert(FileInfo(argv[2]));
std::vector<FileInfo> arr;
DataManager::getInstance()->getFileInfo(&arr);
for(const auto& e : arr)
std::cout << e._backPath << std::endl;
}
return 0;
}
希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!