C++项目——云备份-⑧-客户端各模块实现

news2024/11/19 15:21:20

文章目录

  • 专栏导读
  • 1.客户端数据管理模块实现
  • 2.客户端文件检测模块实现
  • 3.客户端文件备份模块设计
  • 4.客户端文件备份模块实现

专栏导读

🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。

🌸专栏简介:本文收录于 C++项目——云备份

🌸相关专栏推荐:C语言初阶系列C语言进阶系列C++系列数据结构与算法Linux
🌸项目Gitee链接:https://gitee.com/li-yuanjiu/cloud-backup

在这里插入图片描述

1.客户端数据管理模块实现

客户端要实现的功能是对指定文件夹中的文件自动进行备份上传。但是并不是所有的文件每次都需要上传,我们需要能够判断,哪些文件需要上传,哪些不需要,因此需要将备份的文件信息给管理起来,作为下一次文件是否需要备份的判断。因此需要被管理的信息包含以下:

  • 文件路径名称
  • 文件唯一标识:由文件名,最后一次修改时间,文件大小组成的一串信息;

客户端数据管理模块可直接由服务端数据管理模块改造得到,因为其只需要服务端数据管理模块代码中一小部分功能。

#ifndef __MY_DATA__
#define __MY_DATA__
#include <unordered_map>
#include <sstream>
#include "util.hpp"

namespace cloud
{
	class DataManager
	{
	public:
		DataManager(const std::string &backup_file) :_backup_file(backup_file)
		{
			InitLoad();
		}
		bool Storage()
		{
			// 1.获取所有的备份信息
			std::stringstream ss;
			auto it = _table.begin();
			for (; it != _table.end(); ++it)
			{
				// 2.将所有信息进行指定持久化格式的组织
				ss << it->first << " " << it->second << "\n";
			}
			// 3.持久化存储
			FileUtil fu(_backup_file);
			fu.SetContent(ss.str());
			return true;
		}
		bool InitLoad()
		{
			// 1.从文件中读所有数据
			FileUtil fu(_backup_file);
			std::string body;
			fu.GetContent(&body);
			// 2.进行数据解析,添加到表当中
			std::vector<std::string> array;
			Split(body, "\n", &array);
			for (auto& a : array)
			{
				std::vector<std::string> tmp;
				Split(a, " ", &tmp);
				if (tmp.size() != 2)
				{
					continue;
				}
				_table[tmp[0]] = tmp[1];
			}
			return true;
		}
		bool Insert(const std::string &key, const std::string &val)
		{
			_table[key] = val;
			Storage();
			return true;
		}
		bool Updata(const std::string& key, const std::string& val)
		{
			_table[key] = val;
			Storage();
			return true;
		}
		bool GetOneByKey(const std::string& key, std::string* val)
		{
			auto it = _table.find(key);
			if (it == _table.end())
			{
				return false;
			}
			*val = it->second;
			return true;
		}
	private:
		int Split(const std::string &str, const std::string &sep, std::vector<std::string>* array)
		{
			int  count = 0;
			size_t pos = 0, idx = 0;
			while (1)
			{
				pos = str.find(sep, idx);
				if (pos == std::string::npos)
				{
					break;
				}
				if (pos == idx)
				{
					idx = pos + sep.size();
					continue;
				}
				std::string tmp = str.substr(idx, pos - idx);
				array->push_back(tmp);
				count++;
				idx = pos + sep.size();
			}
			if (idx < str.size())
			{
				array->push_back(str.substr(idx));
				count++;
			}
			return count;
		}
	private:
		std::string	 _backup_file; // 备份信息持久化存储文件
		std::unordered_map<std::string, std::string> _table;
	};
}
#endif

2.客户端文件检测模块实现

同样的,客户端文件实用工具类其实与服务端的文件实用工具类雷同,只是功能需求并没有服务端那么多,复制过来即可。

#ifndef __MY_UTIL__
#define __MY_UTIL__
/*
    1.获取文件大小
    2.获取文件最后一次修改时间
    3.获取文件最后一次访问时间
    4.获取文件路径名中的文件名称 /abc/test.txt -> test.txt
    5.向文件写入数据
    6.获取文件数据
    7.获取文件指定位置 指定数据长度
    8.判断文件是否存在
    9.创建目录
    10.浏览获取目录下的所有文件路径名
    11.压缩文件
    12.解压缩所有文件
*/
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <experimental/filesystem>
#include <sys/stat.h>

namespace cloud
{
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    public:
        FileUtil(const std::string &filename) :_filename(filename)
        {}
        int64_t FileSize()
        {
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file size failed!" << std::endl;
                return 0;
            }
            return st.st_size;
        }
        time_t LastMTime()
        {
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get last modify time failed!" << std::endl;
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastATime()
        {
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get last access time failed!" << std::endl;
                return -1;
            }
            return st.st_atime;  
        }
        std::string FileName()
        {
            size_t pos = _filename.find_last_of("/");
            if(pos == std::string::npos)
            {
                return _filename;
            }
            return _filename.substr(pos+1);
        }
        bool GetPosLen(std::string *body, size_t pos, size_t len)
        {
            size_t fsize = FileSize();
            if(pos + len > fsize)
            {
                std::cout << "get file len error" << std::endl;
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename, 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 << "get file content failed" << std::endl;
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        bool GetContent(std::string *body)
        {
            size_t fsize = FileSize();
            return GetPosLen(body, 0, fsize);
        }
        bool SetContent(const std::string &body)
        {
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary);
            if(ofs.is_open() == false)
            {
                std::cout << "write open file failed" << std::endl;
                return false;
            }
            ofs.write(&body[0], body.size());
            if(ofs.good() == false)
            {
                std::cout << "write open file failed" << std::endl;
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        }
       
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool Remove()
        {
            if(Exists() == false)
            {
                return true;
            }
            remove(_filename.c_str());
            return true;
        }
        bool CreateDirectory()
        {
            if(Exists()) return true;
            return fs::create_directories(_filename);
        }
        bool ScanDirectory(std::vector<std::string> *array)
        {
            CreateDirectory();
            for(auto& p : fs::directory_iterator(_filename))
            {
                if(fs::is_directory(p) == true) continue;
                // relative_path 带有路径的文件名
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    private:
        std::string _filename;
    };
}
#endif

3.客户端文件备份模块设计

回顾客户端自动将指定文件夹中的文件备份到服务器流程:

  • 遍历指定文件夹
  • 逐一判断文件是否需要备份
  • 需要备份的文件进行上传备份

客户端文件备份类主要包含以下成员:

#define SERVER_ADDR "47.108.25.253" // 服务器IP
#define SERVER_PORT 8989 // 服务器端口
class Backup
{
public:
	Backup(const std::string& back_dir, const std::string& back_file);
	// 生成文件的唯一标识
	std::string GetFileIdentifier(std::string filename);
	// 上传文件函数
	bool Upload(const std::string& filename); 
	// 判断是否需要上传
	bool IsNeedUpload(const std::string& filename);
	// 主逻辑执行函数
	bool RunMoudle();
private:
	std::string _back_dir; // 监控的文件目录
	DataManager* _data;
};

4.客户端文件备份模块实现

#ifndef __MY_CLOUD__
#define __MY_CLOUD__
#include "data.hpp"
#include "httplib.h"
#include <Windows.h>
#define SERVER_ADDR "47.108.25.253"
#define SERVER_PORT 8989
namespace cloud
{
	class Backup
	{
	public:
		Backup(const std::string& back_dir, const std::string& back_file)
			:_back_dir(back_dir)
		{
			_data = new DataManager(back_file);
		}
		std::string GetFileIdentifier(std::string filename)
		{
			FileUtil fu(filename);
			std::stringstream ss;
			ss << fu.FileName() << "-" << fu.FileSize() << "-" << fu.LastMTime();
			return ss.str();
		}
		bool Upload(const std::string& filename)
		{
			// 1.获取文件数据
			FileUtil fu(filename);
			std::string body;
			fu.GetContent(&body);

			// 2.搭建http客户端上传文件数据
			httplib::Client client(SERVER_ADDR, SERVER_PORT);
			httplib::MultipartFormData item;
			item.content = body;
			item.filename = fu.FileName();
			item.content_type = "application/octet-stream";
			httplib::MultipartFormDataItems items;
			items.push_back(item);

			auto res = client.Post("/upload", items);
			if (!res || res->status != 200)
			{
				return false;
			}
			return true;
		}
		bool IsNeedUpload(const std::string& filename)
		{
			// 需要上传的文件判断条件:文件是新增的,不是新增的但是被修改过
			// 文件是新增的:看一下有没有备份信息
			// 不是新增的但是被修改过:有历史信息,但是历史信息的唯一标识符与当前最新的唯一标识符不一致
			std::string id;
			if (_data->GetOneByKey(filename, &id) != false)
			{
				// 有历史信息
				std::string new_id = GetFileIdentifier(filename);
				if (new_id != id)
				{
					return false;
				}
			}
			//判断一个文件是否有一段时间没有被修改过了
			FileUtil fu(filename);
			if (time(NULL) - fu.LastMTime() < 3)
			{
				return false;
			}
			return true;
		}
		bool RunMoudle()
		{
			while (1)
			{
				// 1.遍历获取指定文件夹中所有文件
				FileUtil fu(_back_dir);
				std::vector<std::string> array;
				fu.ScanDirectory(&array);
				// 2.逐个判断是否需要上传
				for (auto& a : array)
				{
					if (IsNeedUpload(a) == false)
						continue;
					// 3.如果需要,则上传文件
					if (Upload(a) == true)
					{
						_data->Insert(a, GetFileIdentifier(a));
					}
				}
				Sleep(1);
			}
		}
	private:
		std::string _back_dir;
		DataManager* _data;
	};
}
#endif

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

创建一个具有背景轮播和3D卡片翻转效果的个人名片网页

目录 项目展示 图片展示 前言 项目目标 项目目标 步骤 3&#xff1a;CSS 样式 步骤 4&#xff1a;JavaScript 动画 项目源码 知识点介绍 &#xff08;大佬请绕道&#xff09; HTML 结构的构建 2. CSS 样式的设计 3. JavaScript 动画的实现 4. 背景图轮播的逻辑 5…

java智慧工地云平台源码 人工智能AI+多系统集成+智能预警平台

智慧工地云平台源码 人工智能AI多系统集成智能预警平台 智慧工地企业级监管平台融入AIoT、移动互联网和物联网等领先技术&#xff0c;再结合工地“人、机、料、法、环”五大要素&#xff0c;劳务实名制管理、环境监测管理、安全施工管理、质量及能耗管理等智慧化应用&#xff0…

21.8 Python 使用BeautifulSoup库

BeautifulSoup库用于从HTML或XML文件中提取数据。它可以自动将复杂的HTML文档转换为树形结构&#xff0c;并提供简单的方法来搜索文档中的节点&#xff0c;使得我们可以轻松地遍历和修改HTML文档的内容。广泛用于Web爬虫和数据抽取应用程序中。 读者如果需要使用这个库&#x…

Leetcode---368周赛

题目列表 2908. 元素和最小的山形三元组 I 2909. 元素和最小的山形三元组 II 2910. 合法分组的最少组数 2911. 得到 K 个半回文串的最少修改次数 一、元素和最小的山形三元组I 没什么好说的&#xff0c;不会其他方法就直接暴力&#xff0c;时间复杂度O(n^3)&#xff0c;代…

SpringCloudGateway 入门

目录 POM 依赖一、内容网关的作用Spring-Cloud-Gateway的核心概念 二、基于Ribbon的负载均衡三、核心概念详细3.1 断言 Predicate3.2 过滤器3.2.1 内置过滤器3.2.2 自定义过滤器构造器&#xff08;原理&#xff09;资源结构Route / Predicate 的构造器构造器的增强器整体协同关…

人大金仓(Kingbase)部署

点击上方蓝字关注我 1. 介质下载 下载地址&#xff1a;https://www.kingbase.com.cn/rjcxxz/index.htm 选择安装包及授权文件&#xff1a;根据对应的操作系统类型选择安装包 2. 部署环境配置 2.1 部署环境&#xff1a; 8C 16G KylinV10SP3系统 2.2 修改操作系统内核参数 sy…

什么是pmp证书,pmp证书有什么用,pmp项目管理证书的认证考试时间是什么时候啊?

PMP是项目管理证书&#xff0c;目标是项目经理。 英文全称是Project Management Professional&#xff0c;中文全称叫做项目管理专业人士资格认证。 它是由美国项目管理协会&#xff08;PMI&#xff09;在全球范围内推出的针对项目经理的资格认证体系&#xff0c;严格评估项目…

贪心算法总结(未完结)

贪心的定义&#xff08;摘自百度百科&#xff09; 贪心算法&#xff08;greedy algorithm&#xff0c;又称贪婪算法&#xff09;是指&#xff0c;在对问题求解时&#xff0c;总是做出在当前看来是最好的选择。也就是说&#xff0c;不从整体最优上加以考虑&#xff0c;算法得到的…

LeetCode刷题:26. 删除有序数组中的重复项

文章目录 写在前面⭐️26. 删除有序数组中的重复项⭐️&#x1f510;题目描述&#x1f4a1;解题思路&#x1f511;代码 写在前面 本题的题解代码是用C语言编写的。 &#x1f4d2;博客主页&#xff1a;2023Fighting的博客主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f…

Springboot 使用JavaMailSender发送邮件 + Excel附件

目录 1.生成Excel表格 1.依赖设置 2.代码&#xff1a; 2.邮件发送 1.邮件发送功能实现-带附件 2.踩过的坑 1.附件名中文乱码问题 3.参考文章&#xff1a; 需求描述&#xff1a;项目审批完毕后&#xff0c;需要发送邮件通知相关人员&#xff0c;并且要附带数据库表生成的…

[ubuntu系统下的文本编辑器nano,vim,gedit,文件使用,以及版本更新问题]

文本编辑器概要 在Ubuntu系统下&#xff0c;有许多文本编辑器可供选择&#xff0c;每个编辑器都有其独特的特性和用途。以下是一些常见的文本编辑器&#xff1a; Gedit&#xff1a; 这是Ubuntu默认的文本编辑器&#xff0c;它简单易用&#xff0c;适合基本的文本编辑任务。 安…

Java采集传感器数据,亲测有效!

背景 先说背景&#xff0c; 最近公司项目需要用到传感器&#xff0c;采集设备温湿度&#xff0c;倾斜角&#xff0c;电流…&#xff0c;公司采购采购了一个温湿度传感器给我们开发测试使用&#xff0c;如下图&#xff1a; 看着还挺精致有没有。 进入正题 有了这个温湿度传感器…

【Leetcode】【每日一题】【中等】1465. 切割后面积最大的蛋糕

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/maximum-area-of-a-piece-of-cak…

Vue3+Element-Plus项目 el-table 拖拽排序实现,Vue3项目sortablejs的安装与使用

概述 技术栈&#xff1a; Vue3 Ts Vite Element-Plus 实现&#xff1a;实现 sortablejs 实现 el-tabel 的拖拽排序&#xff0c;可滚动排序&#xff0c;并实现拖拽排序的开启与关闭 文章目录 概述一、先看效果二、安装 sortablejs三、sortablejs 封装3.1 utilts 封装3.2 全局…

SpringBoot 定时任务:@EnableScheduling @Scheduled

Scheduled注解参数 cron参数 这个参数是最经常使用的参数&#xff0c;表示接收一个cron参数&#xff0c;cron它是一个表达式&#xff0c;最多接收7个参数&#xff0c;从左到右分别表示&#xff1a;秒 分 时 天 月 周 年&#xff1b;参数以空格隔开&#xff0c;其中年不是必须参…

【JAVA学习笔记】48 - 八大常用Wrapper类(包装类)

一、包装类 1.针对八种基本定义相应的引用类型一包装类 2.有了类的特点&#xff0c;就可以调用类中的方法。 黄色背景的表示父类是Number 二、包装类和基本数据的转换 演示包装类和基本数据类型的相互转换&#xff0c;这里以int和Integer演示。 1.jdk5前的手动装箱和拆箱方…

STM32 TIM(四)编码器接口

STM32 TIM&#xff08;四&#xff09;编码器接口 编码器接口简介 Encoder Interface 编码器接口 编码器接口可接收增量&#xff08;正交&#xff09;编码器的信号&#xff0c;根据编码器旋转产生的正交信号脉冲&#xff0c;自动控制CNT自增或自减&#xff0c;从而指示编码器的…

取Dataset子集(pytorch)

取Dataset子集--pytorch 1. why2. how3. example 1. why 我们在调试深度学习代码时&#xff0c;常常会遇到数据集太大&#xff0c;导致调试浪费时间的情况&#xff0c;这种情况下&#xff0c;将数据集中的一个子集拿出来用于调试代码&#xff0c;调试成功在用完整的数据集运行…

elementUI el-table实现鼠标悬浮某一行,在鼠标右侧展示提示信息

背景 el-table组件中&#xff0c;可以通过勾选某条数据来创建单据&#xff0c;但是有些数据没有权限使用&#xff0c;就需要禁用掉勾选的功能&#xff0c;然后当鼠标悬浮在这一行的时候&#xff0c;展示类似于toolTip的提示框。 除了当鼠标悬浮在某一行&#xff0c;展示类似于…

离散数学速成视频推荐(讲的不错)

【拯救者】离散数学速成(期末考研专升本) 适合人群 期末 考研 复试 转升本 月考都可以用 课程大纲 适用课本 适用于所有离散数学课本 按课本章节来&#xff0c; 抽取重点&#xff0c;翻译为人话 学习步骤&#xff1a; 每一章&#xff0c;都会【讲会考的基础&#x1f4bb;】&a…