【毕业项目】 云备份(二)

简介: 【毕业项目】 云备份(二)

项目编写

工具类设计

我们在实际做项目的使用会用到许多文件操作 我们如果是用到的时候才去写就会导致效率较低并且代码冗余复用率低 因此我们可以在项目编写之前就设计出一个工具类 当我们需要使用该工具类的时候只需要定义一个对象即可

对于该工具类的函数声明如下

/*util.hpp*/
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);
};

下面我会带大家完成整个工具类

属性和名称获取

首先我们定义的函数名称可能会和系统库或者第三方库的名称冲突 所以说我们在完成工具类的时候最好是在一个命名域里面操作

我们在查找文件的各种属性的时候使用的一个系统函数叫做stat

它在windows和Linux平台下都存在 所以说我们不用担心跨平台移植性

stat函数

函数原型如下

int stat(const char* path , struct stat *buf)

参数说明:

  • const char* path 是我们要寻找的路径(是一个字符串)
  • struct stat *buf 这是一个结构体 我们通过该结构体来查看文件的信息

返回值说明:

如果找到该文件返回0 如果没找到返回-1

文件大小获取

      int64_t FileSize()                                            
      {                                                             
        struct stat st;                                             
        int ret = stat(_filename.c_str(), &st);                     
        if (ret < 0)                                                
        {                                                           
          std::cout << "Get file size failed!" << std::endl;        
          return -1;                                                
        }    
        return st.st_size;    
      }


文件最后修改时间获取

      time_t LastModifyTime()
      {
        struct stat st;
        int ret = stat(_filename.c_str() , &st);
        if (ret < 0)
        {
          std::cout << "Get lastmodifytime failed!" << std::endl;
          return -1;
        }
        return st.st_mtime;
      }   

文件最后访问时间获取

      time_t LastAccessTime()                                   
      {                                                         
        struct stat st;                                         
        int ret = stat(_filename.c_str() , &st);                
        if (ret < 0)                                            
        {                                                       
          std::cout << "Get lastaccesstime failed!"  << std::endl;
          return -1;
        }
        return st.st_atime;
      }   

文件名获取

为什么要要获取文件名呢

因为用户在上传文件名的时候可能连带着路径一起上传了 可是我们只需要一个文件名

一般来说Linux下路径的格式如下 xxx/yyy/test.c

如果我们要想获取最后这个文件名我们需要以最右边的 / 的后一个字符开始往后截取所有内容

代码表示如下

      std::string FileName()    
      {    
        size_t pos = _filename.rfind("/");    
        if (pos == std::string::npos)    
        {    
          return _filename;    
        }    
        return _filename.substr(pos+1);                                                                               
      } 

功能测试

这里要跟同学们强调的一点是 我们在自主开发一个项目的时候一定要写一段测试一段

不然到最后可能会出现bug比代码行数都多的现象 这样子调试起来就很头疼了

下面编写一段代码来测试下上面实现的功能

#include "Util.hpp"    
void TestUtil(const std::string& filename)    
{    
  shy::FileUtil fu(filename);    
  std::cout << fu.FileName() << std::endl;    
  std::cout << fu.FileSize() << std::endl;    
  std::cout << fu.LastAccessTime() << std::endl;    
  std::cout << fu.LastModifyTime() << std::endl;    
  return;    
}    
void Useage()    
{    
  std::cout << "./main + FileName" << std::endl;    
  return;    
}    
int main(int argc , char* argv[])    
{                                                                                                                     
  if (argc != 2)    
  {    
    Useage();    
    return -1;    
  }  
  std::string filename = argv[1];
  TestUtil(filename);
  return 0;
}

测试效果如下

8c4893f1c37649a4b344bc915b39f86e.png

我们发现各函数使用效果正常

文件读写操作

获取指定位置往后指定长度的数据

      bool GetPosLen(std::string& body , size_t pos , size_t len)
      {
        size_t fsize = this->FileSize();    
        if (pos + len < fsize)    
        {
          std::cout << "Get file len failed!" << std::endl;    
          return false;    
        }                                                                                                             
        std::ifstream ifs;    
        ifs.open(_filename.c_str() , std::ios::binary);    
        if (ifs.is_open() == false)    
        {    
          std::cout << "open file failed!" <<  std::endl;    
          return false;    
        }  
        ifs.seekg(pos , std::ios::beg);    
        body.resize(len);    
        ifs.read(&body[0] , len);    
        if (ifs.good() == false)    
        {    
          std::cout << "read file failed!" << std::endl;    
          return false;    
        }    
        ifs.close();
        return true;
      }

获取所有文件数据

我们直接复用上面的函数即可

      bool GetContent(std::string& body)
      {
        size_t fsize = this->FileSize();
        return this->GetPosLen(body , 0 , fsize);
      }    

写入文件数据

      bool SetContent(std::string& body)    
      {        
        std::ofstream ofs;    
        ofs.open(_filename.c_str() , std::ios::binary);    
        if (ofs.is_open() == false)    
        {      
          std::cout << "open ofs failed" << std::endl;    
          return false;    
        }      
        ofs.write(&body[0] , body.size());    
        if (ofs.good() == false)    
        {      
          std::cout << "ofs write failed!" << std::endl;    
          return false;    
        }                                                                                                             
        ofs.close();    
        return true;                                                                            
      }    

接下来我们测试上面所写的函数

  std::string body;    
  fu.GetContent(body);    
  shy::FileUtil nfu("test.txt");    
  nfu.SetContent(body);    

我们在当前文件下创建一个叫做test.txt的文件 如果说我们的读写操作没问题 那么我们的源文件里面的内容应该和body里面的内容一致

测试结果如下

b6898069931e4756975bf7257e58de50.png

结果符合预期

文件压缩和解压缩

文件压缩和文件解压我们直接使用Bundle库中的函数即可

代码表示如下

压缩

      bool Compress(const std::string &packname)
      {
        // 1. get source date 
        std::string body;
        if (this->GetContent(body) == false)
        {
          std::cout << "compress get file content failed" << std::endl;
          return false;
        }
        // 2. compress date 
        std::string packed = bundle::pack(bundle::LZIP , body);
        // 3 write comress date to packname 
        FileUtil fu(packname);
        if (fu.SetContent(packed) == false)                                                                           
        {
          std::cout << "write compress date failed" << std::endl;
          return false;
        }
        return true;
      }

解压缩

      bool uncompress(std::string& unpackname)
      {
        // 1. get compress file name 
        std::string body;                                                                                             
        if (this->GetContent(body) == false)
        {
          std::cout << "get Compress file content failed" << std::endl;
          return false;
        }
        // 2 . uncompress file 
        std::string unpacked = bundle::unpack(body);
        // 3. write unpacked file 
        FileUtil fu(unpackname); 
        if (fu.SetContent(unpacked) == false)
        {
          std::cout << "uncompress write unpacked file failed" << std::endl;
          return false;
        }
        return true;
      }

演示效果如下

image.png

我们发现md5值一样 也就是说压缩和解压缩功能正常

目录操作

在C++17版本中 给我们提供了一个很方便的文件操作库

我们可以使用这个库来进行一些文件目录操作

他的头文件为 <experimental/filesystem>

为了简化我们后面的操作 我们将其命名域重新定义

namespace fs = std::experimental::filesystem;

目录是否存在

检查目录是否存在的函数为

bool exists( const path& p );

返回值说明:

返回值是一个bool类型的数据 如果存在返回true 失败返回false

参数说明:

我们传入一个路径名以判断该路径下的最后一个文件是否存在

使用代码表示如下

      bool Exists()    
      {                                                                                                               
        return fs::exists(_filename);    
      }  

创建目录

创建目录的函数为

bool create_directories( const path& p );

返回值说明:

如果创建成功 则直接返回true 否则返回false

参数说明:

传入一个路径名即可

代码表示如下

      bool CreateDirectory()         
      {                              
        if (this -> Exists())        
        {                            
          return true;               
        }                            
        return fs::create_directories(_filename);
      } 

查看目录

我们可以使用迭代器来遍历一个目录 之后查看该目录里面的文件

具体代码表示如下

      bool ScanDirectory(std::vector<std::string>& arry)  
      {  
        for (auto& p : fs::directory_iterator(_filename))  
        {  
          if (fs::is_directory(p) == true)  
          {  
            continue;  
          }  
          // return relative path
          arry.push_back(fs::path(p).relative_path().string());
        }  
        return true;  
      } 

上面的代码中还有几处小细节

  1. 目录中还有可能存在目录 如果是目录则我们不查看
  2. 迭代器本身不是string类型 我们需要通过类型转换之后才能够放进arry数组中

功能测试

在编译之前我们要连接stdc++fs

我们以当面目录下的文件作为测试对象

测试代码如下

void TestUtilDirectory(std::string& filename)
{
  shy::FileUtil fu(filename);
  fu.CreateDirectory();
  if (fu.Exists())
  {
    std::vector<std::string> arry;
    fu.ScanDirectory(arry);
    for (auto& fn : arry)    
    {    
      std::cout << fn << std::endl;    
    }    
  }    
}  

测试结果如下

7ce46de55d6643f2be9284045239742b.png

确实打印出了除目录以外的所有文件路径

json实用工具类设计

我们之前使用json类的时候又是智能指针 又是定义对象的 十分繁琐

为了简化操作 我们这里设计一个json使用工具类

大体格式如下

/*util.hpp*/
class JsonUtil{
 public:
 static bool Serialize(const Json::Value &root, std::string *str);
   static bool UnSerialize(const std::string &str, Json::Value *root);
};

序列化代码如下

      static bool Serialize(const Json::Value& root , std::string& str)    
      {    
         Json::StreamWriterBuilder swb;        
         std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());           
         std::stringstream ss;         
         sw->write(root , &ss);     
         str = ss.str();    
         return true;    
      }   

反序列化代码如下

      static bool UnSerialize(const std::string& str , Json::Value& root)    
      {    
           Json::CharReaderBuilder crb;  // subclass                                                                  
           std::unique_ptr<Json::CharReader> cr(crb.newCharReader());         
           std::string err;        
           bool set = cr->parse(str.c_str() , str.c_str()+str.size() , &root , &err);       
           if (set == false)    
           {    
             return false;
           }
           return true;
      }

测试代码如下

void TestSerialize()    
{    
  const char* name = "xiaoming";    
  int age = 18;    
  float score[3] = {90 , 91 , 98};    
  Json::Value root;    
  root["name"] = name;    
  root["age"] = age;     
  root["score"].append(score[0]);    
  root["score"].append(score[1]);    
  root["score"].append(score[2]);    
  std::string json_str;    
  shy::jsonUtil::Serialize(root , json_str);    
  std::cout << json_str << std::endl;    
} 

配置文件模块

系统配置信息

我们可以将系统需要的一些配置信息加载到文件当中

如果我们这样子做的话 想要修改一些信息我们就只需要修改文件中的信息然后重启服务员即可

这样子就让我们的配置信息变得灵活起来

下面是我们的一些配置信息

热点管理时间

在我们的测试样本中 我们将30s没有访问的文件标志为一个非热点文件 可是在现实中 一个非热点文件可能要在一两天没有被访问之后才会被标记 所以说这是一个多变的数据

文件下载的url前缀路径

url的格式一般是这样子的 http://192.168.1.1:9090/path

而我们这里所说的path实际上是一个相对的根目录 一般是wwwroot

所以说假如我们访问的是 /test.txt的话 我们实际上访问的路径是 .../wwwroot/test.txt

那么当我们要访问一个文件资源是listshow的时候 系统要怎么分别 我是要下载listshow这个文件 还是说我要执行listshow指令来查看服务器上有多少文件呢?


这个时候我们的前缀路径就能很好的解决这个问题 如果说我们访问的前缀加上/download/listshow 就是要下载这个文件 如果没有加则说明我们要执行listshow指令

压缩包后缀名

我们可以根据压缩的规则在文件最后加上后缀 比如说 .zip.lz 等等

上传文件存放路径

它标志着我们的文件存放在哪里 别人请求之后应该到哪里找

压缩文件存放路径

压缩文件存放路径和上传文件存放路径肯定是不能放在一起的 因为别人的文件有可能就是压缩文件

服务端备份信息存放地址

当我们将文件压缩之后如果客户端像我们发送请求 要求我们列出文件信息 显然 我们列出压缩后的信息是很不合理的

所以说 在文件压缩之前 我们应该将文件的信息保存起来 以便于客户端随时查询

服务器的IP地址和端口

当我们要将该服务器部署到另外一台主机的时候 我们只需要修改下配置文件中的IP地址和端口就可以了

单例配置类的设计

我们采用懒汉模式来设计该单例类 所以说会有一些线程安全的问题

所以 除了我们之前的那些数据之外我们还要额外加锁保证线程安全

关于单例模式还不了解的同学可以参考我的这篇博客

特殊类的设计

我们的配置文件是使用json格式书写的 代码如下

{    
  "hot_time" : 30,    
  "server_port" : 8080,    
  "server_ip" : "10.0.8.3",    
  "download_prefix" : "/download/",    
  "packfile_suffix" : ".lz",    
  "pack_dir" : "./packdir/",    
  "back_dir" : "./backdir/",    
  "backup_file" : "./cloud.dat"                                 
}  

接下来是模块类的设计

#pragma once     
#include "Util.hpp"    
#include <mutex>    
using namespace shy;    
namespace cloud    
{    
#define CONFIG_FILE "./cloud.conf"    
  class Config    
  {    
    private:    
      Config()    
      {    
        ReadConfigFile();    
      }    
      static Config* _instance;    
      static std::mutex _mutex;                                                                                                                                                                                                                                                                                                                                                                                                                                       
    private:    
      int _hot_time;    
      int _setver_port;    
      std::string _server_ip;    
      std::string _download_prefix;    
      std::string _packfile_suffix;    
      std::string _pack_dir;    
      std::string _back_dir;    
      std::string _backup_file;    
      bool ReadConfigFile()    
      {    
         FileUtil fu(CONFIG_FILE);    
         std::string body;    
         if (fu.GetContent(body) == false)    
         {    
           std::cout << "load Config file failed" << std::endl;    
           return false;    
         }    
         Json::Value root;    
         if (jsonUtil::UnSerialize(body , root) == false)    
         {    
           return false;    
         }    
         _hot_time = root["hot_time"].asInt();    
         _setver_port = root["setver_port"].asInt();    
         _server_ip = root["server_ip"].asString();    
         _download_prefix = root["download_prefix"].asString();    
         _pack_dir = root["pack_dir"].asString();    
         _back_dir = root["back_dir"].asString();    
         _backup_file = root["backup_file"].asString();    
         return true;    
      }    
    public:    
      static Config* GetInstance()    
      {    
        if (_instance == nullptr)    
        {    
          _mutex.lock();    
          if (_instance == nullptr)    
          {    
            _instance = new Config();    
          }    
          _mutex.unlock();    
         }    
        return _instance;    
      }    
      int GetHotTime()    
      {    
        return _hot_time;    
      }    
     int GetSetverPort()    
     {    
       return _setver_port;    
     }    
     std::string GetServerIP()    
     {    
       return _server_ip;    
     }    
     std::string GetDownloadPrefix()    
     {    
       return _download_prefix;    
     }    
     std::string GetPackfileSuffix()    
     {    
      return _packfile_suffix;    
     }    
    std::string GetPackDir()    
    {    
      return _pack_dir;    
    }    
    std::string GetBackDir()    
    {    
      return _back_dir;    
    }    
    std::string GetBackupFile()    
    {    
     return _backup_file;    
    }    
  };    
  Config* Config::_instance = nullptr;    
  std::mutex Config::_mutex;    
}  

单例配置类的测试

测试的代码也很简单 我们直接使用我们写的cloud类 看看能不能获取各个类型的数据就可以了

  void ConfigTest()
  {
    cloud::Config* config = cloud::Config::GetInstance();                         
    std::cout << config->GetHotTime() << std::endl;
    std::cout << config->GetServerIP() << std::endl;
    std::cout << config->GetSetverPort() << std::endl;
    std::cout << config->GetPackfileSuffix() << std::endl;
  } 


数据管理模块

数据管理模块信息

我们首先要知道我们为什么要设计数据管理模块

对于一些数据来说 我们接收到之后使用一次就可以丢了 那么我们就可以不需要将它管理起来

如果说一个数据我们以后要经常用 或者说必须要用 我们就必须要将它管理起来了

所以说我们设计数据管理模块的根本原因是我们之后要用到这些数据

我们要存放的数据有哪些呢?

  1. 文件的实际存储路径:当客户端需要下载文件的时候 服务器从这个路径读取数据并进行响应
  2. 文件压缩包存放路径名:如果文件是一个非热点文件 那么这个文件就会被压缩 压缩后的文件路径 就是文件压缩包存放路径名 此外如果客户要下载一个压缩文件 我们会先解压缩 并且发送给客户端
  3. 文件是否被压缩的标志位
  1. 文件大小
  2. 文件最后一次修改时间
  3. 文件最后一次访问时间
  4. 文件访问的URL路径 (其实就是我们平时见到的下载路径)

如何管理数据

  1. 用于数据信息访问 :使用hash表在内存中管理数据 以url的path作为key值
  2. 信息的持久化管理 :使用json序列化将所有的数据保存在文件中

数据管理模块设计

我们这里要介绍到一个新的锁的种类 读写锁

pthread_rwlock_t_rwlock 

这个锁的特点是 读共享 写互斥

我们这里之所以不用互斥锁的原因是互斥锁是要串行化的 它的效率特别低

代码表示如下

namespace cloud    
{    
  using namespace shy;    
  typedef struct BackupInfo_t    
  {    
    bool pack_flag;    
    size_t fsize;    
    time_t mtime;    
    time_t atime;    
    std::string real_path;    
    std::string pack_path;    
    std::string url;    
    public:    
    void NewBackupInfo(const std::string& realpath)    
    {    
      Config* config = Config::GetInstance();                                                                                                                                                                                                                   
      std::string packdir = config->GetPackDir();    
      std::string packsuffix = config->GetPackfileSuffix();    
      std::string download_prefix = config->GetDownloadPrefix();    
      FileUtil fu(realpath);    
      this->pack_flag = false;    
      this->fsize = fu.FileSize();    
      this->mtime = fu.LastModifyTime();    
      this->atime = fu.LastAccessTime();    
      this->real_path = realpath;    
      this->pack_path = packdir + fu.FileName() + packsuffix;    
      this->url = download_prefix + fu.FileName();    
    }    
  }BackupInfo;    
}  

当然 写完一段代码之后我们最重要的就是测试了 我们在main函数中使用该类并且依次打印其所有信息看看是否有错漏

在此之前 因为每次都要编译bundle.cpp库都需要花费很多的时间 我们可以将其生成一个静态库来使用

具体生成静态库的方法可以参考我的这篇博客 动静态库

测试代码如下

void DataTest(const std::string& filename)                           
{    
  cloud::BackupInfo info;    
  info.NewBackupInfo(filename);    
  std::cout << info.pack_flag << std::endl;    
  std::cout << info.fsize << std::endl;    
  std::cout << info.mtime << std::endl;    
  std::cout << info.atime << std::endl;    
  std::cout << info.real_path << std::endl;    
  std::cout << info.pack_path << std::endl;    
  std::cout << info.url << std::endl;    
}   

测试结果如下

aa2634960ec34bdeba8bb07bb79c549d.png

数据管理类的实现

数据管理类中要使用哈希表存储数据 并且要使用读写锁

      std::string _backup_file;
      pthread_rwlock_t _rwlock;
      std::unordered_map<std::string,BackupInfo> _table;


在数据管理类中 我们要实现 插入 更新 读取操作 实现函数如下

      bool Insert(const BackupInfo& info)
      {
        pthread_rwlock_wrlock(&_rwlock);
        _table[info.url] = info;
        pthread_rwlock_unlock(&_rwlock);
        return true;
      }
      bool Update(const BackupInfo& info)
      {
        pthread_rwlock_wrlock(&_rwlock);                                                       
        _table[info.url] = info;    
        pthread_rwlock_unlock(&_rwlock);    
        return true;    
      }
      bool GetOneByURL(const std::string& url , BackupInfo* info)                              
      {
        pthread_rwlock_rdlock(&_rwlock);
        auto it = _table.find(url);
        if (it == _table.end())
        {
          return false;
        }
        *info = it->second;
        pthread_rwlock_unlock(&_rwlock);
        return true;
      }
      bool GetOneByRealPath(const std::string& realpath, BackupInfo* info)
      {
        pthread_rwlock_wrlock(&_rwlock);                                                       
        auto it = _table.begin();
        while (it != _table.end())
        {
          if (it->second.real_path == realpath)
          {
            *info = it->second;
            pthread_rwlock_unlock(&_rwlock);
            return true;
          }
          it++;
        }
        pthread_rwlock_unlock(&_rwlock);
        return false;
      }
      bool GetAll(std::vector<BackupInfo>* array)
      {
        pthread_rwlock_wrlock(&_rwlock);
        auto it = _table.begin();
        while (it != _table.end())
        {
          array->push_back(it->second);
        }
        pthread_rwlock_unlock(&_rwlock);
        return true;
      }

数据管理类的测试

接下来我们在主函数中对于数据管理类进行测试

代码表示如下

void DataTest(const std::string& filename)    
{    
  cloud::BackupInfo info;    
  info.NewBackupInfo(filename);    
  cloud::DataManager data;    
  data.Insert(info);    
  cloud::BackupInfo tmp;    
  data.GetOneByURL("/download/bundle.h", &tmp);    
  std::cout << "test getonebyrul" << std::endl;    
  std::cout << tmp.pack_flag << std::endl;    
  std::cout << tmp.fsize << std::endl;    
  std::cout << tmp.mtime << std::endl;    
  std::cout << tmp.atime << std::endl;    
  std::cout << tmp.real_path << std::endl;    
  std::cout << tmp.pack_path << std::endl;    
  std::cout << tmp.url << std::endl;    
  info.pack_flag = true;    
  data.Update(info);    
  std::vector<cloud::BackupInfo> arry;    
  data.GetAll(&arry);    
  std::cout << "test getall" << std::endl;    
  for (auto x : arry)    
  {    
    std::cout << x.pack_flag << std::endl;    
    std::cout << x.fsize << std::endl;    
    std::cout << x.mtime << std::endl;    
    std::cout << x.atime << std::endl;    
    std::cout << x.real_path << std::endl;    
    std::cout << x.pack_path << std::endl;    
    std::cout << x.url << std::endl;    
  }    
  data.GetOneByRealPath(tmp.real_path ,&tmp);    
  std::cout << "test getonebyrealpath" << std::endl;    
  std::cout << "--------------------------" << std::endl;    
  std::cout << tmp.pack_flag << std::endl;    
  std::cout << tmp.fsize << std::endl;    
  std::cout << tmp.mtime << std::endl;    
  std::cout << tmp.atime << std::endl;    
  std::cout << tmp.real_path << std::endl;    
  std::cout << tmp.pack_path << std::endl;                                                                                                                                                                                                                                                                                                                            
  std::cout << tmp.url << std::endl;    
}    


测试结果如下dbcb5f3d33004183ad915d9313b4c4b3.png

可以正常运行

数据的持久化存储

我们的程序有可能关机 但是在下次开机的时候我们可能还是会需要这些数据 所以说我们需要将这些数据进行持久化存储

在前面的第一个项目httpsever中 我们使用mysql存储数据

那么在这个项目中 我们就使用文件来存储数据

持久化存储分为四步

  • 获取所有数据
  • 使用json格式存储
  • 序列化数据
  • 写入文件
        bool Storage()    
        {    
          // 1. get all date    
          std::vector<BackupInfo> arry;    
          this->GetAll(&arry);    
          // 2. add to json::value    
          Json::Value items;    
W>        for (int i = 0; i < arry.size(); i++)    
          {    
            Json::Value root;    
            root["pack_flag"] = arry[i].pack_flag;    
            root["fszie"] = std::to_string(arry[i].fsize);    
            root["atime"] = std::to_string(arry[i].atime);    
            root["mtime"] = std::to_string(arry[i].mtime);    
            root["real_path"] = arry[i].real_path;    
            root["pack_path"] = arry[i].pack_path;    
            root["url"] = arry[i].url;    
            items.append(root); // append array elements                                                                                                                           
          }    
          // 3. serialize the items     
          std::string body;    
          jsonUtil::Serialize(items , body);    
          // 4. write the file     
          FileUtil fu(_backup_file);    
          fu.SetContent(body);    
          return true;    
        }   

数据的持久化存储测试

当我们在测试函数内部调用该函数的时候 就会生成一个cloud.dat文件用来作为持久化存储

文件如下

02131249cf1b47809f858b10b53400c4.png

初始化从文件中读取数据

从文件中读取数据分为三步

  • 从cloud.dat中读取数据
  • 反序列化
  • 添加值到table中
        bool InitLoad()
        {
          // 1 get data from cloud.dat 
          FileUtil fu(_backup_file);
          if (fu.Exists() == false)                                                                                                                  
          {
            return true;
          }
          std::string body;
          fu.GetContent(body);
          // 2 unserialize 
          Json::Value root;
          jsonUtil::UnSerialize(body , root);
          // 3 add value to table 
W>        for (int i = 0; i < root.size(); i++)
          {
            BackupInfo info;
            info.pack_flag = root[i]["pack_flag"].asBool();
            info.fsize = root[i]["fszie"].asInt();
            info.mtime = root[i]["mtime"].asInt();
            info.atime = root[i]["atime"].asInt();
            info.pack_path = root[i]["pack_path"].asString();
            info.real_path = root[i]["real_path"].asString();
            info.url = root[i]["url"].asString();
            Insert(info);
          }
        }

这里我们需要注意的是 如果出现类型转化的错误 我们可以使用强制类型转换来解决

测试结果如下

c2e5205d67fe4b25b1eeb4ee6aa67566.png

cloud.dat 中的数据相同

b7d7325b7582471c8134bdf702ac1878.png

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
相关文章
|
6月前
|
人工智能 云计算
阿里云产品手册2025版发布
阿里云产品手册2025版发布,涵盖阿里云产品大图、面向 AI 的全栈云计算产品体系等最新内容,囊括了阿里云产品介绍、优势、功能、应用场景和发展历程的介绍。
637 12
|
12月前
|
存储
在使用 realloc 函数时,如何避免数据丢失?
在使用 realloc 函数动态调整内存大小时,为避免数据丢失,应先将原指针保存到临时变量中,调用 realloc 后检查返回值是否为 NULL,若为 NULL 则保留原指针,否则更新指针并释放临时变量。
|
11月前
|
数据采集 文字识别 测试技术
智源研究院发布千万级多模态指令数据集Infinity-MM:驱动开源模型迈向SOTA性能
近年来,视觉语言模型(VLM)取得了显著进展,然而,现有的开源数据和指令数据集在数量和质量上依然落后,基于开源数据训练的模型在效果上仍然远落后于 SOTA 闭源模型或使用专有数据训练的开源模型。为解决以上问题,进一步提升开源模型的性能,2024年10月25日,智源研究院发布并开源了千万级多模态指令数据集Infinity-MM。
|
8月前
|
安全 Java 调度
Java中简单定时任务的实现
在 Java 中实现定时任务有多种方式,每种方式的原理和适用场景不同。`java.util.Timer` 和 `TimerTask` 通过单线程执行任务,适合简单场景;`ScheduledExecutorService` 基于线程池,支持多线程并发,更加灵活和健壮;三种方式各有优劣,开发者可根据需求选择合适的方案。
|
10月前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
246 3
|
人工智能 Rust JavaScript
Github 2024-08-26 开源项目周报Top15
根据Github Trendings的统计,本周共有15个项目上榜。以下是按开发语言汇总的项目数量:Python项目8个,TypeScript、C++ 和 Rust 项目各2个,Jupyter Notebook、Shell、Swift 和 Dart 项目各1个。其中,RustDesk 是一款用 Rust 编写的开源远程桌面软件,可作为 TeamViewer 的替代品;Whisper 是一个通用的语音识别模型,基于大规模音频数据集训练而成;初学者的生成式人工智能(第2版)则是由微软提供的18门课程,教授构建生成式AI应用所需的知识。
364 1
|
算法 Unix Linux
select函数中的文件描述符(File Descriptor)范围
select函数中的文件描述符(File Descriptor)范围
280 0
select函数中的文件描述符(File Descriptor)范围
|
Java
JAVA 获取 URL 指定参数的值
JAVA 获取 URL 指定参数的值
169 0
|
缓存 负载均衡 安全
Servlet与JSP在Java Web应用中的性能调优策略
【6月更文挑战第23天】在Java Web中,Servlet和JSP调优至关重要,以应对高并发和复杂业务带来的性能挑战。优化包括Servlet复用、线程安全、数据库连接池,以及JSP的编译优化、使用JSTL、页面缓存和静态内容分离。全局优化涉及负载均衡、异步处理和缓存策略。通过这些实践,开发者能提升应用响应速度和吞吐量,确保高负载下的稳定运行。
262 7
|
机器学习/深度学习 存储 算法
Python 机器学习算法交易实用指南(一)(2)
Python 机器学习算法交易实用指南(一)
377 2