【云备份】服务端⽂件⼯具类设计FileUtil.hpp

1.新接口

int stat(const char *pathname, struct stat *statbuf);

在这里插入图片描述

struct stat
{
    dev_t st_dev;            /* ID of device containing file */
    ino_t st_ino;            /* Inode number */
    mode_t st_mode;          /* File type and mode */
    nlink_t st_nlink;        /* Number of hard links */
    uid_t st_uid;            /* User ID of owner */
    gid_t st_gid;            /* Group ID of owner */
    dev_t st_rdev;           /* Device ID (if special file) */
    off_t st_size;           /* Total size, in bytes */
    blksize_t st_blksize;    /* Block size for filesystem I/O */
    blkcnt_t st_blocks;      /* Number of 512B blocks allocated */
    struct timespec st_atim; /* Time of last access 最后一次访问*/
    struct timespec st_mtim; /* Time of last modification 最后一次修改*/
    struct timespec st_ctim; /* Time of last status change 最后一次状态改变*/

#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};

函数讲解

stat 函数
int stat(const char *pathname, struct stat *statbuf); 函数用于获取指定文件的状态信息,并将其存储在 struct stat 类型的 statbuf 中。

参数说明
pathname:文件的路径名。
statbuf:指向 struct stat 类型的指针,用于接收文件的状态信息。
返回值
成功时返回 0。
失败时返回 -1,并设置 errno 来指示错误类型。

回顾缺省参数

在函数调用时,参数是自左向右逐个匹配的,如果缺省参数不是从右往左给,当实参和形参个数不一致时,可能会导致无法明确参数的对应关系。

回顾C++IO流ifstream

#include <vector>
#include <string>  // for string
#include <fstream> // for ifstream
#include <cstring> // for cerr
#include <iostream>
int main()
{
    // 打开文件
    // std::ifstream file("test1.txt", std::ios::binary|std::ios::in);
    std::ifstream file("test1.txt", std::ios::binary);
    if (!file.is_open())
    {
        std::cerr << "Failed to open the file." << std::endl;
        return 1;
    }

    // 读取文件内容到string对象ok
    std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
    
    // 逐行读取ok
    // std::string content;
    // std::string line;
    // while (std::getline(file, line))
    // {
    //     content += line;
    //     content += "\n"; // getline不读\n
    // }

    // read函数ok
    // std::string content;
    // content.resize(100);
    // file.read(&content[0], 100);

    // 输出读取的内容(可选)
    std::cout << content << std::endl;

    // 关闭文件
    file.close();
    return 0;
}

回顾Linux三个系统时间

一、文件时间的作用和应用场景
在 Linux 系统中,文件的最后一次访问时间(atime)、最后一次修改时间(mtime)和最后一次状态改变时间(ctime)都具有重要的作用和特定的应用场景。

(一)最后一次访问时间(atime)

  • 作用:记录文件内容被读取的时间。
  • 应用场景:
    • 可以用于监测文件是否被读取,例如某些安全监控系统中,通过检查 atime 来判断是否有异常的文件访问行为。
    • 对于缓存系统,可根据 atime 来决定是否需要更新缓存内容。

(二)最后一次修改时间(mtime)

  • 作用:反映文件内容的最后修改时刻
  • 应用场景:
    • 备份系统可以依据 mtime 来确定哪些文件需要备份。
    • 版本控制系统中,通过对比 mtime 来判断文件是否有更新,从而决定是否需要进行版本控制操作。

(三)最后一次状态改变时间(ctime)

  • 作用:记录文件的 i 节点(inode)最后一次被修改的时间。
  • 应用场景:
    • 当文件的属性(如权限、所有者等)发生变化时,ctime 会更新,这对于权限管理和审计非常有用。
    • 一些系统管理工具可以利用 ctime 来监测文件系统的状态变化。

C++17std::experimental::filesystem

c++17std::experimental::filesystem

exists

show case

#include <cstdint>
#include <experimental/filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::experimental::filesystem;
 
void demo_exists(const fs::path& p, fs::file_status s = fs::file_status{})
{
    std::cout << p;
    if (fs::status_known(s) ? fs::exists(s) : fs::exists(p))
        std::cout << " exists\n";
    else
        std::cout << " does not exist\n";
}
 
int main()
{
    fs::create_directory("sandbox");
    std::ofstream("sandbox/file"); // create regular file
    fs::create_symlink("non-existing", "sandbox/symlink");
 
    demo_exists("sandbox");
    for (auto it = fs::directory_iterator("sandbox"); it != fs::directory_iterator(); ++it)
        demo_exists(*it, it->status()); // use cached status from directory entry
    fs::remove_all("sandbox");
}


"sandbox" exists
"sandbox/file" exists
"sandbox/symlink" does not exist

create_directory 与 create_directories

show case

#include <cstdlib>
#include <experimental/filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::experimental::filesystem;
 
int main()
{
    fs::create_directories("sandbox/1/2/a");//如果路径中的任何父目录不存在 一并创建。
    fs::create_directory("sandbox/1/2/b");//前提是路径中的所有父目录已经存在
    fs::permissions("sandbox/1/2/b", fs::perms::remove_perms | fs::perms::others_all);//移除其他用户的所有权限
    fs::create_directory("sandbox/1/2/c", "sandbox/1/2/b");//以sandbox/1/2/b的属性创建目录sandbox/1/2/c
    std::system("ls -l sandbox/1/2");//调用系统命令ls
    fs::remove_all("sandbox");//递归地删除sandbox目录及其所有内容
}



drwxr-xr-x 2 user group 4096 Apr 15 09:33 a
drwxr-x--- 2 user group 4096 Apr 15 09:33 b
drwxr-x--- 2 user group 4096 Apr 15 09:33 c

directory_iterator 与 relative_path() 与 is_directory

#include <experimental/filesystem>
#include <fstream>
#include <iostream>
#include <typeinfo>
// g++ test.cc -o test -std=c++17 -lstdc++fs
namespace fs = std::experimental::filesystem;

int main()
{
    // 1.测试directory_iterator
    std::cout << "测试directory_iterator" << std::endl;

    fs::create_directories("sandbox/a/b");
    std::ofstream{"sandbox/file1.txt"};
    std::ofstream{"sandbox/file2.txt"};
    for (const fs::directory_entry &entry : fs::directory_iterator{"sandbox"})
        std::cout << entry << '\n';
    fs::remove_all("sandbox");
    /*
    "sandbox/a"
    "sandbox/file2.txt"
    "sandbox/file1.txt"
    */
    std::cout << std::endl;
    // 2.测试relative_path()
    std::cout << "测试relative_path()" << std::endl;

    fs::path curPath = fs::current_path();
    std::cout << "The current path " << curPath << " decomposes into:\n"
              << "root-path " << curPath.root_path() << '\n'
              << "relative-path " << curPath.relative_path() << '\n';
    /*
    The current path "C:\Users\abcdef\Local Settings\temp" decomposes into:
    root-path "C:\"
    relative-path "Users\abcdef\Local Settings\temp"
    */
    std::cout << std::endl;

    // 3.测试is_directory
    std::cout << "测试is_directory" << std::endl;

    fs::path p = "./test1.txt";
    fs::path p2 = "../server";
    if (fs::is_directory(p))
        std::cout << p << " is a directory." << std::endl;
    else
        std::cout << p << " is not a directory." << std::endl;

    if (fs::is_directory(p2))
        std::cout << p2 << " is a directory." << std::endl;
    else
        std::cout << p2 << " is not a directory." << std::endl;
    return 0;
}

c语言库函数remove

在这里插入图片描述

2.整体代码

// 当文件增多 极有可能出现头文件重复包含 使用预处理指令
// 使得当该头文件已被包含时 不再重复包含该文件
#ifndef __MY_FILEUTIL__
#define __MY_FILEUTIL__

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <experimental/filesystem>
#include <sys/stat.h>
#include "./mylib/include/bundle.h"
#include "log.hpp"

// 同一个工程中允许存在多个相同名称的命名空间 最后合成同一个命名空间
namespace cloudBackup
{
	// 简洁命名
	namespace filesystem = std::experimental::filesystem;

	class FileUtil
	{
	private:
		std::string _filename;

	public:
		FileUtil(const std::string &filename)
			: _filename(filename)
		{
		}

		// 获取文件大小
		int64_t FileSize()
		{
			struct stat statBuf;
			// int stat(const char *file, stat *buf)
			if (stat(_filename.c_str(), &statBuf) < 0)
			{
				Log::log(Error, "FileUtil::stat-get file size failed!: %s: %d", strerror(errno), errno);
				return -1;
			}
			return statBuf.st_size;
		}

		// 由文件路径获取纯文件名称
		std::string FileName()
		{
			// ./a/b/c/test.txt --> test.txt
			size_t pos = _filename.find_last_of("/");
			if (pos == std::string::npos) // 没找到/ 说明本就是不带路径的文件
				return _filename;

			return _filename.substr(pos + 1);
		}

		// 获取最后一次访问时间
		time_t LastATime()
		{
			struct stat statBuf;
			// int stat(const char *file, stat *buf)
			if (stat(_filename.c_str(), &statBuf) < 0)
			{
				Log::log(Error, "FileUtil::stat-get last_atime failed!: %s: %d", strerror(errno), errno);
				return -1;
			}
			return statBuf.st_atime;
		}

		// 获取最后一次状态改变时间
		time_t LastCTime()
		{
			struct stat statBuf;
			// int stat(const char *file, stat *buf)
			if (stat(_filename.c_str(), &statBuf) < 0)
			{
				Log::log(Error, "FileUtil::stat-get last_ctime failed!: %s: %d", strerror(errno), errno);
				return -1;
			}
			return statBuf.st_ctime;
		}

		// 获取最后一次修改时间
		time_t LastMTime()
		{
			struct stat statBuf;
			// int stat(const char *file, stat *buf)
			if (stat(_filename.c_str(), &statBuf) < 0)
			{
				Log::log(Error, "FileUtil::stat-get last_mtime failed!: %s: %d", strerror(errno), errno);
				return -1;
			}
			return statBuf.st_mtime;
		}

		// 获取文件pos后len长度的数据 传递给content
		bool GetPosLen(std::string *content, size_t pos, size_t len)
		{
			// pos后文件长度不足len
			size_t fsize = this->FileSize();
			if (pos + len > fsize)
			{
				Log::log(Error, "FileUtil::GetPosLen pos + len > fsize !: %s: %d", strerror(errno), errno);
				return false;
			}

			std::ifstream ifs;
			ifs.open(_filename, std::ios::binary);
			if (ifs.is_open() == false)
			{
				Log::log(Error, "FileUtil::GetPosLen::ifs.is_open() failed !: %s: %d", strerror(errno), errno);
				return false;
			}

			ifs.seekg(pos, std::ios::beg);
			content->resize(len);
			// &(*content)[0] == &( (*content)[0] ) 这么做是因为read接收char*类型
			ifs.read(&(*content)[0], len);
			// 检查在读取文件内容的过程中是否发生了错误
			if (ifs.good() == false)
			{
				Log::log(Error, "FileUtil::GetPosLen::ifs.good()-get file content failed !: %s: %d", strerror(errno), errno);
				ifs.close();
				return false;
			}
			ifs.close();
			return true;
		}

		// 获取整个文件内容传递给content #代码复用
		bool GetContent(std::string *content)
		{
			size_t fsize = this->FileSize();
			return GetPosLen(content, 0, fsize);
		}

		// 将content中的数据写入到文件
		bool SetContent(const std::string &content)
		{
			std::ofstream ofs;
			ofs.open(_filename, std::ios::binary);
			if (ofs.is_open() == false)
			{
				Log::log(Error, "FileUtil::SetContent::ofs.is_open()-open file failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			ofs.write(&content[0], content.size());
			if (ofs.good() == false)
			{
				Log::log(Error, "FileUtil::SetContent::ofs.good()-write file content failed !: %s: %d", strerror(errno), errno);
				ofs.close();
				return false;
			}
			ofs.close();
			return true;
		}

		// 将成员属性_filename中的数据压缩后存入zipFile
		bool Compress(const std::string &zipFile)
		{
			// 1. 获取原文件内容存入到content binary->string
			std::string content;
			if (this->GetContent(&content) == false)
			{
				Log::log(Error, "FileUtil::Compress::GetContent()-get file content failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			// 2. 对数据进行压缩 string->string.zip
			std::string packed = bundle::pack(bundle::LZIP, content);
			// 3. 将压缩后的数据存储到压缩包文件中
			FileUtil fileUtil(zipFile);
			if (fileUtil.SetContent(packed) == false)
			{
				Log::log(Error, "FileUtil::Compress::SetContent()-write packed data failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			return true;
		}

		// 将成员属性_filename中的数据解压后存入zipFile
		bool DeCompress(const std::string &unzipFile)
		{
			// 读取成员属性_filename压缩包数据存入content
			std::string content;
			if (this->GetContent(&content) == false)
			{
				Log::log(Error, "FileUtil::DeCompress::GetContent()-get file content failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			// 对压缩包数据进行解压缩
			std::string unpacked = bundle::unpack(content);
			// 将解压缩的数据写入到新文件unzipFile
			FileUtil fileUtil(unzipFile);
			if (fileUtil.SetContent(unpacked) == false)
			{
				Log::log(Error, "FileUtil::DeCompress::SetContent()-write unpacked data failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			return true;
		}

		// 判断文件是否存在
		bool Exists(filesystem::file_status status = filesystem::file_status{})
		{
			// bool exists(const path& p);
			if (filesystem::status_known(status) ? filesystem::exists(status) : filesystem::exists(_filename))
				return true;
			else
				return false;
		}

		// 删除某个文件
		bool Remove()
		{
			if (this->Exists() == false)
				return true;

			// int remove(const char *pathname);
			remove(_filename.c_str());
			return true;
		}

		// 创建目录 目录是一个可以包含文件的文件
		bool CreateDirectory()
		{
			if (this->Exists())
				return true;
			// 路径中的任何父目录不存在 一并创建
			return filesystem::create_directories(_filename);
		}

		// 浏览_filename这个 目录 下所有文件名 存入fileArray
		bool ScanDirectory(std::vector<std::string> *fileArray)
		{
			// const fs::directory_entry &entry
			for (const auto &entry : filesystem::directory_iterator(_filename))
			{
				//如果浏览到了目录 则跳过 (我们只需要获取文件)
				if (filesystem::is_directory(entry) == true)
					continue;

				// relative_path 不带根路径的路径
				fileArray->push_back(filesystem::path(entry).relative_path().string());
			}
			return true;
		}
	};
}

#endif //__MY_FILEUTIL__

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿猿收手吧!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值