Linux(CentOS)/Windows-C++ 云备份项目(客户端文件操作类,数据管理模块设计,文件客户端类设计)

文章目录

  • 1. 客户端文件操作类
  • 2. 客户端数据管理模块设计
  • 3. 文件客户端类设计
  • 项目代码

客户端负责的功能

  • 指定目录的文件检测,获取文件夹里面的文件

  • 判断这个文件是否需要备份,服务器备份过的文件则不需要进行备份,已经备份的文件如果修改也需要重新备份

  • 若这个文件被用户打开,则不进行备份。需要每隔一段时间检测更新备份。

  • 将需要备份的文件上传备份文件

客户端功能模块划分

  1. 数据管理模块:管理备份的文件信息
  2. 文件检测模块:监控指定文件夹,获取这个文件夹下所有的文件信息(通过获取到的备份文件信息)
  3. 文件备份模块:上传需要备份的文件数据,将数据传递给服务器

数据管理模块要管理的数据:

  1. . 判断一个文件是否需要重新备份
  2. 文件路径名,文件唯一标识符

客户端的开发环境在Windows上,使用vs2022(支持C++17即可 vs2017以上)

数据管理模块实现:

  • 内存存储:使用哈希表
  • 持久化存储:文件存储,自定义序列化格式(key Value型)

1. 客户端文件操作类

客户端数据管理模块设计和服务器相差不大可以直接移植

#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include "log.hpp"
// #include <filesystem>
#include <experimental/filesystem>
namespace CloudBackups
{namespace fs = std::experimental::filesystem;class FileUtil{private:std::string _filepath; // 文件名称 uri格式struct stat st;        // 文件属性public:FileUtil() = default;FileUtil(const std::string &filepath){_filepath = filepath;if (stat(_filepath.c_str(), &st) < 0){LOG(WARNING, "get file stat failed! maybe this file not exits");}}int64_t filesize() { return st.st_size; }         // 获取文件大小,失败返回-1time_t last_modify_time() { return st.st_mtime; } // 获取文件最后修改时间time_t last_visit_time() { return st.st_atime; }  // 获取文件最后访问时间// 删除文件bool removeFile(){if (this->isExit() == false){return false;}if (remove(_filepath.c_str()) == 0){return true;}else{return false;}}std::string filename() // 文件名称{size_t pos = _filepath.find_last_of("/");if (pos == std::string::npos){return _filepath;}return _filepath.substr(pos + 1);}bool getPoslen(std::string &body, size_t pos, size_t len) // 从文件中读取len个字节,从pos位置开始读取,读取内容放到body中,为了实现断点续传{size_t size = this->filesize(); // 文件大小if (pos >= size){LOG(ERROR, "pos is out of range!");return false;}if (pos + len > size){LOG(ERROR, "pos + len is out of range!");return false;}std::ifstream ifs;ifs.open(_filepath.c_str(), std::ios::binary);if (!ifs.is_open()){LOG(ERROR, "open file failed!");return false;}ifs.seekg(pos, std::ios::beg);body.resize(len);ifs.read(&body[0], len);if (!ifs.good()){// 上次读取出错LOG(ERROR, "read file failed!");ifs.close();return false;}ifs.close();return true;}bool getContent(std::string &body) // 获取整体的文件数据{size_t size = this->filesize();return getPoslen(body, 0, size);}bool setContent(const std::string &body) // 设置文件内容{std::ofstream ofs;ofs.open(_filepath.c_str(), std::ios::binary);if (!ofs.is_open()){LOG(ERROR, "open file failed! file path=" + _filepath);return false;}ofs.write(body.c_str(), body.size());if (!ofs.good()){// 上次写入出错LOG(ERROR, "write file failed!");ofs.close();return false;}ofs.close();return true;}bool isExit() { return fs::exists(_filepath); } // 判断文件是否存在bool mkdir()                                    // 创建文件夹{if (this->isExit()){return true;}return fs::create_directories(_filepath);}bool mkdir(const std::string &path) // 创建文件夹{if (this->isExit()){return true;}return fs::create_directories(path);}bool ls(std::vector<std::string> &files) // 扫描文件夹,并返回里面的文件{for (auto &pos : fs::directory_iterator(_filepath)){if (fs::is_directory(pos) == true){continue; // 目录不出来}files.push_back(fs::path(pos).relative_path().string()); // 获取文件的相对路径}return true;}};
}

2. 客户端数据管理模块设计

#pragma once
#include<string>
#include<unordered_map>
#include<sstream>
#include"fileutil.hpp"
namespace CloudBackups {class DataManager {private:std::string backup_file;//备份信息持久化文件,需要在创建类时加载std::unordered_map<std::string, std::string>backupMap;//管理文件路径和文件信息的映射public:// 切分字符串src,结果放到buff上bool cutString(std::string& src, const std::string sep, std::vector<std::string>&buff){size_t pos = 0;size_t index = 0;while (true){pos = src.find(sep, index);if (pos == std::string::npos) {break;}if (pos == index) {index = pos + sep.size();continue;}std::string str = src.substr(index, pos - index);buff.push_back(str);index = pos + sep.size();}if (index < src.size()) {//还剩数据buff.push_back(src.substr(index));}return true;}DataManager(const std::string backup_file) {this->backup_file = backup_file;InitLoad();}bool Storage() {//1. 获取所有备份信息auto pos = backupMap.begin();std::stringstream builder;while (pos != backupMap.end()) {//2. 将所有信息组织成特定格式builder << pos->first << " " << pos->second << "\n";pos++;}//3. 将所有信息写入文件FileUtil tool(backup_file);tool.setContent(builder.str());return true;}bool InitLoad() {//1. 从文件中读取所有数据FileUtil tool(backup_file);std::string body;tool.getContent(body);//2. 对数据进行解析,添加到backupMap中//按照行进行分割,每行按照空格分割std::vector<std::string>files;cutString(body, "\n", files);for (auto& file : files) {std::vector<std::string>buff;cutString(file, " ", buff);if (buff.size() != 2) {// LOG(ERROR, "cut string error!");return false;}backupMap[buff[0]] = buff[1];}}bool Insert(const std::string& key, const std::string& value) {backupMap[key] = value;Storage();return true;}bool UpDate(const std::string& key, const std::string& value) {backupMap[key] = value;Storage();return true;}bool GetByKey(const std::string& key, std::string& value) {auto pos = backupMap.find(key);if (pos == backupMap.end()) {return false;}value = pos->second;return true;}};
}

3. 文件客户端类设计

客户端实现:

  1. 自动将指定文件夹的备份文件备份到服务器上

流程:

  1. 遍历指定文件夹,获取文件信息
  2. 逐一判断文件是否需要备份,对需要备份的文件上传备份

需要注意的是:若文件比较大,正在拷贝过程,每次遍历文件都被修改,每次客户端都会上传不合理,所以要设置在一段时间没有被修改则上传

#pragma once
#include<string>
#include"backups.hpp"
#include<vector>
#include<sstream>
#include"httplib/httplib.h"
#include<Windows.h>
#define SEVER_IP "116.204.70.147"
#define SEVER_PORT 8081
namespace CloudBackups {class Client {private:std::string back_dir;DataManager* dataMange;//判断文件标识符是否需要上传bool CheckFileUpload(std::string filepath) {//文件新增或文件修改过都需要上传 1. 文件新增:看下文件备份信息 2. 文件有历史信息,但是文件标识符不一致std::string pre_id;if (dataMange->GetByKey(filepath, pre_id) != false) {//判断文件更新std::string id = GetEtag(filepath);if (id != pre_id) {//前后信息不一致,需要上传//文件比较大,正在拷贝过程,每次遍历文件都被修改,每次客户端都需要上传不合理,所以要设置在一段时间没有被修改则上传FileUtil tool(filepath);if (time(nullptr) - tool.last_modify_time() > 3) {//3秒之内未修改,需要上传给服务器return true;}}return false;//前后信息一致或者客户端文件拷贝未完成,不需要上传}//文件不存在,需要上传return true;}public:Client(const std::string& back_dir, const std::string& back_file) {this->back_dir = back_dir;dataMange = new DataManager(back_file);//创建上传目录文件夹FileUtil tool;tool.mkdir(back_dir);}//创建文件唯一标识符std::string GetEtag(const std::string& filepath) {//文件名-文件大小-修改时间FileUtil tool(filepath);std::stringstream builder;builder << tool.filename() << "-" << tool.filesize() << "-" << tool.last_modify_time();return builder.str();}//文件上传接口bool Upload(const std::string filepath) {//获取文件数据FileUtil tool(filepath);std::string body;tool.getContent(body);//搭建客户端发送请求httplib::Client client(SEVER_IP, SEVER_PORT);httplib::MultipartFormData item;item.content = body;//item.filename = tool.filename();item.name = "file";//服务器上标识的是file,需要和服务器协商item.content_type = "application/octet-stream";httplib::MultipartFormDataItems items;items.push_back(item);auto ret = client.Post("/upload", items);if (!ret || ret->status != 200) {LOG(ERROR, "file upload error!");return false;}return true;}//服务器运行bool RunModule() {while (true) {//1. 浏览需要备份的文件FileUtil tool(back_dir);std::vector<std::string>files;tool.ls(files);for (auto& file : files) {//2. 逐个判断文件需要上传if (CheckFileUpload(file)==false) {//不需要上传continue;}//需要上传服务器if (Upload(file) == true) {dataMange->Insert(file, GetEtag(file));//上传服务器完毕后写入文件配置文件LOG(INFO, "upload success!");}else {LOG(ERROR, "upload error! filepath: " + file);}}Sleep(10);//毫秒为单位}return true;}};
}

在这里插入图片描述
在这里插入图片描述

项目代码

Gitee

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/575761.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024-03-28 Quest3 开发环境配置教程

文章目录 准备条件1 登录 Meta 账号2 Oculus 软件下载与配置3 下载 Quest3 开发包4 Unity 环境配置环境测试 准备条件 Quest3 头显一个。一根 USB 3.0 数据线。魔法。 ​ 有关 quest3 激活与配置可参考 B 站 UP &#xff1a;“南七月nqy_”。跳转链接&#xff1a;https://spa…

代码随想录day30(2)回溯:组合(leetcode77)

题目要求&#xff1a;给定两个整数 n 和 k&#xff0c;返回 1 ... n 中所有可能的 k 个数的组合。 思路&#xff1a;首先定义两个变量&#xff0c;一个存放符合条件的单一结果&#xff0c;另一个存放符合条件结果的集合&#xff0c;for循环用来横向遍历&#xff0c;递归用来纵…

Postman Tests设置Global读取不是最新值,跟Tests执行顺序有关(踩坑笔记)

前言 在执行Run Collection的时候&#xff0c;发现设置的全局变量每次读取都是旧值&#xff0c;没有读取到最新的值。 背景 有2个地方需要动态参数&#xff0c;一个URL&#xff0c;一个Body&#xff0c;因此需要设置Tests脚本。 url动态参数 url&#xff1a;动态参数projec…

目标检测的相关模型图:YOLO系列和RCNN系列

目标检测的相关模型图&#xff1a;YOLO系列和RCNN系列 前言YOLO系列的图展示YOLOpassthroughYOLO2YOLO3YOLO4YOLO5 RCNN系列的图展示有关目标检测发展的 前言 最近好像大家也都在写毕业论文&#xff0c;前段时间跟朋友聊天&#xff0c;突然想起自己之前写画了一些关于YOLO、Fa…

第5章.零、单例与小样本提示词的编写之道

零提示、单个提示和小样本提示是用于从ChatGPT中生成文本的技术。在数据匮乏或任务全新、定义模糊之时&#xff0c;我们用微妙的提示&#xff0c;让ChatGPT从无到有&#xff0c;生成文本。 面对任务&#xff0c;空无一例&#xff1a;模型凭借对任务的广泛理解&#xff0c;独辟…

鸿蒙OS开发实例:【手撸服务卡片】

介绍 服务卡片指导文档位于“开发/应用模型/Stage模型开发指导/Stage模型应用组件”路径下&#xff0c;说明其极其重要。 本篇文章将分享实现服务卡片的过程和代码 准备 请参照[官方指导]&#xff0c;创建一个Demo工程&#xff0c;选择Stage模型 鸿蒙OS开发更多内容↓点击…

1学习使用axios

一、axios介绍&#xff1a; axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;用于浏览器和 Node.js。它提供了一种简单的方法来发送 HTTP 请求&#xff0c;并且具有很多实用的功能&#xff0c;使得网络请求变得更加方便和可靠。 以下是 axios 的一些主要特点和功能&…

Machine Learning机器学习之统计分析

目录 前言 机器学习之统计分析 统计学的主要目标包括&#xff1a; 统计学核心概念&#xff1a; 统计基础&#xff1a; 训练误差&#xff1a; 常见的损失函数&#xff1a; 正则化和交叉验证 博主介绍&#xff1a;✌专注于前后端、机器学习、人工智能应用领域开发的优质创作者、秉…

如何制作蛋糕店小程序_开启您的蛋糕店小程序之旅

甜蜜滋味&#xff0c;一触即达——开启您的蛋糕店小程序之旅 在这个快节奏的时代&#xff0c;人们对美食的追求从未停歇。尤其是那些色香味俱佳、口感细腻的蛋糕&#xff0c;更是成为了许多人生活中的小确幸。然而&#xff0c;忙碌的工作和生活常常让我们无法亲自前往蛋糕店&a…

关于web_server项目的学习记录(自用)

主要参考资料&#xff1a; 我在地铁吃闸机 基础处理框架&#xff1a;Multi-reactor muduo库有三个核心组件实现持续监听reactor的fd&#xff1a;channel;epoll/poller/eventloop类 channel 事件监听器epoll_ctl监听到了fd发生了什么事件,channel类会封装每个fd和fd感兴趣的事…

C# OpenCv Haar、LBP 人脸检测

目录 效果 代码 下载 效果 代码 using OpenCvSharp;namespace OPenCVDemo {class Program{static void Main(string[] args){// Load the cascadesvar haarCascade new CascadeClassifier("haarcascade_frontalface_default.xml");var lbpCascade new Casca…

网络原理-传输层-UDP报文结构

本文介绍UDP报文 有很多友友搞不清楚UDP报文的详细结构还有TCP的详细结构,所以专门分开来讲 以免弄混. 首先我们先看一下整个UDP结构,让大家有一个全方面的认识 下面我们来详细解释UDP报 16位源端口号(本机):就是2字节大小,16个二进制位. 16位目的端口号(目的机):也是2字节…