🎁个人主页 :离芩
🔍系列专栏:Linux实战
🎉欢迎大家点赞👍评论📝收藏⭐文章 !!!
hello !大家好呀! 欢迎大家来到我的Linux项目实战系列之《【百度云盘项目实践(2)】:文件实用类工具设计》,在这篇文章中,你将会学习到在云盘项目中,我们怎样去管理我们的文件,我会手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!!
希望这篇文章能对你有所帮助,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!(注:这章对于高性能服务器的实现非常重要哟!!!)
![]()
目录
🎉 2.2 time_t LastTime()和string FileName();
🎉 3.1 bool GetPosLen(std:: string *body , size_t pos ,size_t len)
🎉3.2 bool SetContent(const std::string &body)
一.文件管理类设计思想
对与文件的管理,我们需要设计一个实用类来进行文件管理,其中包括文件信息的获取,修改,以及文件的压缩与解压缩,文件目录的创建,资源的访问,以及json数据序列号与反序列化的接口包装。
因此我们依据上面要求,设计出这样一个类:
这个文件管理类中的数据成员只有一个,也就是文件名,我们根据这个类中的文件名对文件进行管理,包括类的实例化,文件大小的获取,文件修改时间与最新访问时间,文件名的获取,获取指定大小区间的文件内容,设置文件内容,文件的压缩与解压缩,目录的创建与浏览。
这些函数的实现都使用了许多外接库与接口,接下来我就给大家详细解读这些函数的实现与运用吧!!!
二.文件信息获取函数设计
对于文件信息的获取,我们需要实现文件大小的获取,文件修改时间与最新访问时间,文件名的获取。
2.1 int64_t FileSize();
这是文件大小的获取函数:
int64_t FileSize(){
struct stat st;//获取文件信息接口
if(stat(_filename.c_str() , &st)< 0){
return -1;
std::cout<< "Get file_size failed\n";
return -1;
}
return st.st_size;//返回文件大小
}//获取文件大小
我们实用了stat函数,可以直接获取整个文件的信息,需要了解这个函数的可以去看我之前的博客。
2.2 time_t LastTime()和string FileName();
这是文件最后的修改时间和访问时间的获取,与上面函数一样,使用stat函数即可:
time_t LastTime(){
struct stat st;//获取文件信息接口
if(stat(_filename.c_str() , &st)< 0){
return -1;
std::cout<< "Get file_size failed\n";
return -1;
}
return st.st_mtime;
}//文件最后一次修改时间
time_t LastATime(){
struct stat st;//获取文件信息接口
if(stat(_filename.c_str() , &st)< 0){
return -1;
std::cout<< "Get file_size failed\n";
return -1;
}
return st.st_atime;
}//文件最后一次访问时间
2.3 string FileName() ;
这个函数用于获取在给定一个路径下,获取我们目标文件名称,意思是只要文件名,前面的路径都给去除,这个函数虽然简单但是却十分重要的。
std::string FileName(){
// ./abc/test.tst
size_t pos = _filename.find_last_of("/");//找到路径的最后一个斜杠
if(pos == std::string::npos){//本身就是文件名
return _filename;
}
return _filename.substr(pos+1);
}//文件名称获取
三.文件内容获取与修改函数设计
3.1 bool GetPosLen(std:: string *body , size_t pos ,size_t len)
bool GetPosLen(std:: string *body , size_t pos ,size_t len){//获取指定长度的数据
std::ifstream ifs;
ifs.open(_filename , std::ios::binary);//二进制打开文件
if(ifs.is_open() == false){
std::cout<< "open file failed\n";
return false ;
}
size_t fsize = this->FileSize();//获取当前文件大小
if(pos + len > fsize){//超出文件大小
std::cout<<" get file len is erron\n";
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\n";
ifs.close();
return false;
}
ifs.close();
return true;
}
这个函数`GetPosLen`的目的是从一个文件中读取指定位置和长度的数据。
1. 函数参数
- `std::string *body`: 一个指向`std::string`的指针,用于存储从文件中读取的数据。
- `size_t pos`: 指定从文件开始的读取位置。
- `size_t len`: 指定要读取的数据长度。
2. 文件操作
- 使用`std::ifstream`类打开一个文件(文件名是类的一个成员变量`_filename`),以二进制模式打开。
- 检查文件是否成功打开。如果打开失败,打印错误信息并返回`false`。
3. 检查读取长度
- 通过调用`FileSize()`函数(这也是类的一个成员函数)获取文件大小。
- 检查请求的读取位置加上长度是否超出文件大小。如果超出,打印错误信息并返回`false`。
4. 读取数据
- 使用`seekg()`函数将文件读指针定位到指定的位置`pos`。
- 调整`body`指向的`std::string`的大小,使其等于`len`。
- 使用`read()`函数从文件中读取`len`长度的数据到`body`指向的`std::string`中。
- 检查读取操作是否成功。如果失败,打印错误信息,关闭文件,并返回`false`。
5. 结束操作
- 关闭文件。
- 如果所有操作都成功,返回`true`。
这个函数主要用于从大型文件中安全地读取特定部分的数据,适用于需要处理大文件或需要从特定位置读取数据的应用场景。
3.2 bool SetContent(const std::string &body)
这是向文件内写入数据,代码与查看相似,这里就不多解释了,有不懂的可以直接百度
bool GetContent(std:: string *body){//获取文件全部内容
size_t fsize = this->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 failed.\n";
return false;
}
ofs.write(&body[0] , body.size());//写入数据
if(ofs.good() == false){
std::cout<<"write file content failed!\n";
ofs.close();
return false;
}
ofs.close();
return true;
}
四.压缩与解压缩
对于文件的压缩与解压缩,这是云盘处理文件的一个很重要的功能,对于一些大型文件与非热点文件,我们服务器一般都会备份压缩。
压缩与解压缩接口实现:
bool Compress(const std::string &packname){//压缩功能
//1.读取源文件数据
std::string body;
if(this->GetContent(&body) == false){
std::cout<< "compress get file content failen!\n";
return false;
}
//2.对文件进行压缩
std::string packed = bundle::pack(bundle::LZIP, body);
//将压缩的数据存入数据包文件
FileUtil fu(packname);
if(fu.SetContent(packed) == false){
std::cout<<" compress write packed failed!\n";
return false;
}
return true;
}
bool uncompress(const std::string &filenmae){
//1.将当前压缩包数据读取
std::string body;
if(this->GetContent(&body) == false){
std::cout<<"uncompress write packed data failed!\n";
return false;
}
//将压缩数据解压缩
std::string unpacked = bundle::unpack(body);
//将解压缩数据写入到新文件
FileUtil fu(filenmae);
if(fu.SetContent(unpacked) == false){
std::cout<<" uncompress write packed failed!\n";
return false;
}
return true;
}
这两个函数`Compress`和`uncompress`分别用于文件的压缩和解压缩。
1. `Compress`函数
这个函数的目的是将当前文件压缩,并保存到指定的压缩包文件中。
- **读取源文件数据**:
- 调用`GetContent`函数(假设这是类的一个成员函数)来读取整个文件的内容到`body`字符串中。如果读取失败,打印错误信息并返回`false`。
- **文件压缩**:
- 使用`bundle::pack(bundle::LZIP, body)`函数(假设这是定义在某个命名空间`bundle`中的一个函数,用于压缩数据)对读取到的数据进行压缩。这里使用的是LZIP压缩算法。
- **保存压缩数据**:
- 使用`FileUtil`类(假设这是一个用于文件操作的辅助类)的实例`fu`,调用`SetContent`方法将压缩后的数据`packed`写入到指定的压缩包文件`packname`中。如果写入失败,打印错误信息并返回`false`。
- **返回结果**:
- 如果所有操作都成功,返回`true`。
2. `uncompress`函数
这个函数的目的是将压缩包中的文件解压缩,并保存到指定的文件中。
- **读取压缩包数据**:
- 调用`GetContent`函数读取压缩包文件的内容到`body`字符串中。如果读取失败,打印错误信息并返回`false`。
- **文件解压缩**:
- 使用`bundle::unpack(body)`函数(假设这是定义在`bundle`命名空间中的一个函数,用于解压缩数据)对读取到的数据进行解压缩。
- **保存解压缩数据**:
- 使用`FileUtil`类的实例`fu`,调用`SetContent`方法将解压缩后的数据`unpacked`写入到指定的文件`filenmae`中。如果写入失败,打印错误信息并返回`false`。
- **返回结果**:
- 如果所有操作都成功,返回`true`。
这两个函数共同构成了一个简单的文件压缩和解压缩工具,可以用于处理文件的压缩与解压操作。
具体内容可以参考我上一篇博客:【百度云盘项目实践(1)】:探索JSON与Bundle库的结合应用-CSDN博客
五.目录功能函数设计
对于云盘文件管理,我们需要用户在下载或者查看文件内容时,给予用户方便的目录进行查看和下载,接口函数实现如下:
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;
}
//relative_path 带有路劲的文件名
arry->push_back(fs::path(p).relative_path().string());
}
return true;
}
};
1. CreateDirectory
函数
这个函数的目的是创建一个目录。
-
检查目录是否存在:
- 调用
Exists
函数(假设这是类的一个成员函数)来检查指定的目录(_filename
,这是类的一个成员变量)是否存在。如果目录已经存在,直接返回true
。
- 调用
-
创建目录:
- 如果目录不存在,使用
std::filesystem::create_directories
函数(这是C++17中引入的文件系统库中的函数)来创建目录。这个函数会创建所有必要的中间目录,所以即使多层目录不存在,也会被创建。 - 函数返回创建操作的结果(
true
表示成功,false
表示失败)。
- 如果目录不存在,使用
2. ScanDirectory
函数
这个函数的目的是列出指定目录下的所有文件,但不包括子目录。
-
遍历目录:
- 使用
std::filesystem::directory_iterator
来遍历指定目录(_filename
)中的所有条目。
- 使用
-
检查条目类型:
- 对于每个条目,使用
std::filesystem::is_directory
函数检查它是否是一个目录。如果是目录,则跳过,继续下一次循环。
- 对于每个条目,使用
-
获取文件名:
- 对于不是目录的条目,使用
std::filesystem::path
来获取其路径,并调用relative_path()
方法来获取相对于当前目录的路径。 - 将文件名(相对路径)转换为字符串,并添加到
arry
向量中。
- 对于不是目录的条目,使用
-
返回结果:
- 函数总是返回
true
,不论操作是否成功。这可能是一个设计上的考虑,或者可能是一个错误,因为通常情况下,如果目录不存在或者无法遍历,应该返回false
。
- 函数总是返回
这两个函数可以用于文件系统操作,如创建目录和扫描目录内容,常用于文件管理和资源组织等场景。
好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家还有不懂得,可以评论区或者私信我都可以哦
!! 感谢大家的阅读,我还会持续创造网络编程相
关内容的,记得点点小爱心和关注哟!
![]()