当前位置: 首页 > news >正文

项目日记 -云备份 -服务端数据管理模块

博客主页:【夜泉_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 用来解决读写者问题
    其中updateinsert函数,
    会修改数据,加写锁,
    getInfoByURLgetInfoBybackPathgetFileInfo 函数,
    只读取数据,加读锁。
    然后然后,为了避免误操作,
    我搞了两个类:

    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,哈希表,
    kstring _urlvstruct 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语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

相关文章:

  • Qt Concurrent 并发 Map 和 Map-Reduce
  • Chat2DB:一款强大的数据库管理工具,AI助力高效查询与分析
  • 如何使用Python爬虫按关键字搜索1688商品?
  • SQL HAVING 1 的用法解析
  • 鸿蒙开发:父组件如何调用子组件中的方法?
  • python tkinter 开发蓍草占卜系统
  • Vue3企业级项目标准化规范
  • 蓝桥杯二分法例题--跳石头
  • Windows 下使用 Docker 部署 Go 应用与 Nginx 详细教程
  • 【大模型基础_毛玉仁】4.1 参数高效微调简介
  • 【区块链 + 文化版权】文创链 | FISCO BCOS 应用案例
  • AI Agent 是什么?从 Chatbot 到自动化 Agent(LangChain、AutoGPT、BabyAGI)
  • SpringMVC的搭建及配置
  • Axure项目实战:智慧城市APP(六)市民互动(动态面板、显示与隐藏)
  • Python 标准库与数据结构
  • 104.二叉树的最大深度
  • ngx_http_index_set_index
  • 【逆向】国家能源局gm2
  • 一套云HIS系统源码,系统融合HIS与EMR,基于云端部署,采用B/S架构与SaaS模式
  • Flutter项目之table页面实现
  • 以军证实空袭也门多个港口
  • 英德宣布开发射程超2000公里导弹,以防务合作加强安全、促进经济
  • 普京调整俄陆军高层人事任命
  • 横跨万里穿越百年,《受到召唤·敦煌》中张艺兴一人分饰两角
  • 受贿3501万余元,中石油原董事长王宜林一审被判13年
  • 反制美国钢铝关税!印度拟对美国部分商品征收关税