4. 文件工具類的設計
4.1 整體的類
該類實現對文件進行操作
FileUtil.hpp
如下
/*
該類實現對文件進行操作
*/
#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <experimental/filesystem> // c++17 的文件系統庫
#include "bundle.h"namespace cloud
{
namespace fs = std::experimental::filesystem;class FileUtil
{
private:std::string _fileName;struct stat _st; // 用于獲取文件的各種屬性
public:// 輸入一個路徑FileUtil(std::string fileName) : _fileName(fileName) {if(stat(_fileName.c_str(), &_st) < 0) {std::cerr << "獲取文件屬性失敗!\nstat(_fileName.c_str(), &_st) error! why: " << strerror(errno) << '\n';// exit(1);}}// 獲取文件大小(單位是字節)int64_t getFileSize(){return _st.st_size; // 返回字節數,有符號的長整型}// 獲取文件最后一次修改時間time_t getLastMTime(){return _st.st_mtime;}// 獲取文件最后一次訪問時間time_t getLastATime(){return _st.st_atime;}// 獲得一個路徑最后文件的名稱,例如/abc/a.txt -> a.txtstd::string getFileName(){ssize_t pos = _fileName.find_last_of('/');if(pos == std::string::npos) {// 沒有/,證明這就是一個文件,沒有文件夾return _fileName;}// 有/,要截取字符串return _fileName.substr(pos+1);}// 從pos位置獲取len長度的數據(單位是字節),寫到s中bool getContentFromPos(std::string *s, size_t pos, size_t len){int64_t fileSz = getFileSize();if(pos + len > fileSz) {// 申請的長度大于文件長度std::cerr << "Bad size!\nbool getContentFromPos(std::string *s, size_t pos, size_t len)\n";return false;}// 用來讀取文件std::ifstream ifile(_fileName, std::ios::binary);// 是否打開成功if(ifile.is_open() == false) {std::cerr << "Open file error!\nbool getContentFromPos(std::string *s, size_t pos, size_t len)\n";ifile.close();return false;}// 讀取文件內容ifile.seekg(pos, std::ios::beg);s->resize(len); ifile.read((char*)(s->c_str()), len);// 檢測這個流的狀態是否okif(ifile.good() == false) {std::cerr << "read file error!\nbool getContentFromPos(std::string *s, size_t pos, size_t len)\n";ifile.close();return false;}return true;}// 獲取文件的所有內容,寫到s中bool getFullContent(std::string *s) {// 調用getContentFromPos()return getContentFromPos(s, 0, getFileSize());}// 將字符串s的內容寫入到文件中bool setContent(const std::string& s){// 用來像文件寫入std::ofstream ofile(_fileName, std::ios::binary);// 是否打開成功if(ofile.is_open() == false) {std::cerr << "Open file error!\nbool setContent(const std::string& s)\n";ofile.close();return false;}ofile.write((char*)s.c_str(), s.size());// 檢測這個流的狀態是否okif(ofile.good() == false) {std::cerr << "write file error!\nbool setContent(const std::string& s)\n";ofile.close();return false;}return true;}// 壓縮,packName是壓縮包的名字bool compresss(const std::string& packName){// 先讀取文件內容std::string content;if(getFullContent(&content) == false) {std::cerr << "bool compresss(const std::string& packName) get content error!\n";return false;}// 壓縮文件內容,這里使用LZIP壓縮格式std::string packContent = bundle::pack(bundle::LZIP, content);cloud::FileUtil newFile(packName);// 將內容寫到新的文件if(newFile.setContent(packContent) == false) {std::cerr << "bool compresss(const std::string& packName) set content error!\n";return false;}return true;}// 解壓,fileName是解壓后新文件的名字bool unCompress(const std::string& fileName){// 先讀取文件內容std::string content;if(getFullContent(&content) == false) {std::cerr << "bool unCompress(const std::string& packName) get content error!\n";return false;}// 解壓文件內容std::string unpackContent = bundle::unpack(content);cloud::FileUtil newFile(fileName);// 將內容寫到新的文件if(newFile.setContent(unpackContent) == false) {std::cerr << "bool unCompresss(const std::string& packName) set content error!\n";return false;}return true;}// 判斷文件是否存在,存在返回truebool exits(){return fs::exists(_fileName);}// 創建目錄,創建成功返回truebool createDir(){// 如果該文件存在了,就直接返回trueif(exits()) return true;return fs::create_directories(_fileName);}// 掃描文件夾下的文件,放到數組中bool scanDir(std::vector<std::string> *array){ for (const fs::directory_entry& entry : fs::directory_iterator(_fileName)) {if(fs::is_directory(entry) == true) {// 如果是目錄的話,跳過,該函數只掃描一般文件continue;}array->push_back(entry.path().relative_path().string());} }
};}
makefile如下
cloud : Cloud.cpp bundle.cpp FileUtil.hppg++ $^ -o $@ -lpthread -lstdc++fs.PHONY : clean
clean:rm -f cloud
4.2 測試
4.2.1 測試獲取文件屬性功能
Cloud.cpp
#include "FileUtil.hpp"// 測試獲取文件屬性功能
void testFileUtil(const std::string& s)
{cloud::FileUtil f(s);printf("文件大小: %ld\n", f.getFileSize());printf("文件最后一次修改時間: %ld\n", f.getLastMTime());printf("文件最后一次訪問時間: %ld\n", f.getLastATime());printf("獲得一個路徑最后文件的名稱: %s\n", f.getFileName().c_str());
}int main(int argc, char* argv[])
{if(argc != 2) {std::cerr << "usage error!\n"; return -1;}testFileUtil(argv[1]);return 0;
}
結果如下
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ./cloud test.txt
文件大小: 28
文件最后一次修改時間: 1734504144
文件最后一次訪問時間: 1734504157
獲得一個路徑最后文件的名稱: test.txt
---------------------------------------------------------------------------
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ./cloud ../../lib/bundle/README.md
文件大小: 16571
文件最后一次修改時間: 1734185453
文件最后一次訪問時間: 1734185453
獲得一個路徑最后文件的名稱: README.md
---------------------------------------------------------------------------
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ./cloud aaaa.txt
獲取文件屬性失敗!
stat(_fileName.c_str(), &_st) error! why: No such file or directory
4.2.2 測試文件讀寫功能
void testFileUtil2(const std::string& fileName)
{cloud::FileUtil f(fileName);std::string s;f.getFullContent(&s);// std::cout << s;cloud::FileUtil of("writeTest.txt"); // 打開一個名為writeTest.txt的文件,沒有則創建of.setContent(s);
}int main(int argc, char* argv[])
{if(argc != 2) {std::cerr << "usage error!\n"; return -1;}testFileUtil2(argv[1]);return 0;
}
目前有個文件test.txt
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ cat test.txt
abcdefghigklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
---------------------------------------------------------------------------
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ./cloud test.txt
獲取文件屬性失敗!
stat(_fileName.c_str(), &_st) error! why: No such file or directory
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ll
total 52
drwxrwxr-x 2 lyf lyf 4096 Dec 18 16:20 ./
drwxrwxr-x 23 lyf lyf 4096 Dec 18 11:31 ../
-rwxrwxr-x 1 lyf lyf 24144 Dec 18 16:20 cloud*
-rw-rw-r-- 1 lyf lyf 969 Dec 18 16:16 Cloud.cpp
-rw-rw-r-- 1 lyf lyf 3516 Dec 18 16:20 FileUtil.hpp
-rw-rw-r-- 1 lyf lyf 67 Dec 18 16:14 makefile
-rw-rw-r-- 1 lyf lyf 53 Dec 18 16:14 test.txt
-rw-rw-r-- 1 lyf lyf 53 Dec 18 16:20 writeTest.txt
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ cat writeTest.txt
abcdefghigklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
4.2.3 測試壓縮和解壓縮功能
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ./cloud bundle.cpp
獲取文件屬性失敗!
stat(_fileName.c_str(), &_st) error! why: No such file or directory
獲取文件屬性失敗!
stat(_fileName.c_str(), &_st) error! why: No such file or directory
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ls -lh
total 15M
-rw-rw-r-- 1 lyf lyf 5.4M Dec 18 19:14 bundle.cpp
-rw-rw-r-- 1 lyf lyf 668K Dec 18 19:34 bundle.cpp.lz
-rw-rw-r-- 1 lyf lyf 29K Dec 18 19:14 bundle.h
-rwxrwxr-x 1 lyf lyf 3.3M Dec 18 19:33 cloud
-rw-rw-r-- 1 lyf lyf 1.3K Dec 18 19:33 Cloud.cpp
-rw-rw-r-- 1 lyf lyf 5.0K Dec 18 19:24 FileUtil.hpp
-rw-rw-r-- 1 lyf lyf 101 Dec 18 19:28 makefile
-rw-rw-r-- 1 lyf lyf 5.4M Dec 18 19:34 newname.cpp
-rw-rw-r-- 1 lyf lyf 23 Dec 18 19:32 test.txt
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ md5sum bundle.cpp
4cb64c7a8354c82402dd6fe080703650 bundle.cpp
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ md5sum newname.cpp
4cb64c7a8354c82402dd6fe080703650 newname.cpp
4.2.4 測試目錄功能
// 測試目錄操作
void testFileUtil4(const std::string& fileName)
{cloud::FileUtil f(fileName);f.createDir(); // 創建一個文件夾std::vector<std::string> array;f.scanDir(&array);// 打印目錄下的文件內容for(const auto& e : array) {std::cout << e << '\n';}
}
# 獲取文件夾下的所有文件
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ls
bundle.cpp bundle.h cloud Cloud.cpp FileUtil.hpp makefile test.txt
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ./cloud test
獲取文件屬性失敗!
stat(_fileName.c_str(), &_st) error! why: No such file or directory
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ls
bundle.cpp bundle.h cloud Cloud.cpp FileUtil.hpp makefile test test.txt
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ cd test/
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份/test$ ll
total 8
drwxrwxr-x 2 lyf lyf 4096 Dec 18 21:53 ./
drwxrwxr-x 3 lyf lyf 4096 Dec 18 21:53 ../
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份/test$ touch a.txt
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份/test$ touch b.cc
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份/test$ touch c.java
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份/test$ touch d.python
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份/test$ cd ../
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ./cloud ./test
./test/c.java
./test/d.python
./test/b.cc
./test/a.txt
5. Json工具類的設計
5.1 整體的類
#pragma once
#include <iostream>
#include <jsoncpp/json/json.h>
#include <string>
#include <sstream>
#include <memory>namespace cloud
{
class JsonUtil
{
public:// 將root的序列化結果保存在str中static bool serialize(const Json::Value& root, std::string* str){Json::StreamWriterBuilder swb;std::unique_ptr<Json::StreamWriter> ps(swb.newStreamWriter());std::stringstream ss;if (ps->write(root, &ss) != 0) {std:: cerr << "write error!\nstatic bool serialize(const Json::Value& root, std::string* str)\n";return false;} *str = ss.str();return true;}// 將str的序列化結果保存在root中static bool unserialize(const std::string& str, Json::Value* root){Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> ps(crb.newCharReader());std::string errs;if (ps->parse(str.c_str(), str.c_str() + str.size(), root, &errs) == false) {std::cerr << "parse error!, why: " << str << "\nstatic bool unserialize(const std::string& str, Json::Value* root)\n";return false;}return true;}
};
}
5.2 測試序列化工具類
// 測試JsonUtil
void testJsonUtil()
{/* 序列化 */std::string name = "zhangsan";int age = 20;int scores[] = {80, 90, 100};// 給數據對象類添加數據Json::Value value;value["name"] = name;value["age"] = age;value["score"].append(scores[0]);value["score"].append(scores[1]);value["score"].append(scores[2]);std::string str;cloud::JsonUtil::serialize(value, &str);printf("序列化結果:\n %s\n%s\n", str.c_str(), "========================================");Json::Value oValue;/* 反序列化, 將s反序列化*/cloud::JsonUtil::unserialize(str, &oValue);printf("反序列化結果: \nname: %s\nage: %d\n", (oValue["name"].asString()).c_str(), oValue["age"].asInt());for(int i = 0; i < 3; ++i) {printf("成績%d: %d\n", i, oValue["score"][i].asInt());}
}
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ./cloud
序列化結果:{"age" : 20,"name" : "zhangsan","score" : [80,90,100]
}
========================================
反序列化結果:
name: zhangsan
age: 20
成績0: 80
成績1: 90
成績2: 100
6. 服務端所用到的配置信息
6.1 json格式的配置信息
-
服務器訪問 IP 地址
-
服務器訪問端?
-
熱點判斷時間(多長時間沒有被訪問的文件屬于非熱點文件)
-
URL路徑前綴(如
http://www.example.com/path/to/file
的路徑前綴是/path/
) -
壓縮包后綴名稱 (在文件原名后面加上該后綴)
-
上傳?件存放路徑(上傳后的文件儲存在服務器的哪個文件夾下)
-
壓縮?件存放路徑(壓縮文件儲存在服務器的哪個文件夾下)
-
服務端備份信息存放文件名 (可以通過數據庫存放)
使用json
格式存放,下面是一個實例。cloud.conf
TODO: 構造函數出錯返回?
將bundle變成一個庫
{"serverIp" : "124.70.203.1","serverPort" : 9000,"hotTime" : 30,"pathPre" : "/listShow/","rarSuf" : ".lz","ulPath" : "./files","rarPath" : "./rars","backups" : "./backups.data"
}
6.2 加載配置信息類
6.2.1 完整的類
使用單例模式。config.hpp
/* 用于加載配置文件 */
#include "FileUtil.hpp"
#include "JsonUtil.hpp"
#include <mutex>
#define CONF_PATH "./cloud.conf"namespace cloud
{
class Config
{
private:static Config* _instance;static std::mutex _mtx;Config(const Config& c) = delete;Config& operator=(const Config& c) = delete;Config() {if(loadFiles() == false) delInstance();}// 從配置文件中加載數據,放到各個屬性中bool loadFiles(){ // 打開文件cloud::FileUtil fu(CONF_PATH);std::string content;if(fu.getFullContent(&content) == false) {std::cerr << "getFullContent error!\nbool loadFiles()\n";return false;}// json轉換Json::Value root;if(cloud::JsonUtil::unserialize(content, &root) == false) {std::cerr << "unserialize error!\nbool loadFiles()\n";return false;}// 初始化_serverIp = root["serverIp"].asString();_serverPort = root["serverPort"].asInt();_hotTime = root["hotTime"].asInt64();_pathPre = root["pathPre"].asString();_rarSuf = root["rarSuf"].asString();_ulPath = root["ulPath"].asString();_rarPath = root["rarPath"].asString();_backups = root["backups"].asString();return true;}
private:std::string _serverIp; // 服務器訪問 IP 地址 int _serverPort; // 服務器訪問端? std::time_t _hotTime; // 熱點判斷時間std::string _pathPre; // URL路徑前綴std::string _rarSuf; // 壓縮包后綴名稱std::string _ulPath; // 上傳文件存放的路徑std::string _rarPath; // 壓縮文件存放的路徑std::string _backups; // 備份文件
public:// 獲取實例static Config* getInstance(){if(_instance == nullptr) { // _instance為null時,才有加鎖解鎖的必要。外面多加一層判斷可以防止每次getInstance()時都要申請鎖std::unique_lock<std::mutex> lck(_mtx);if(_instance == nullptr) {_instance = new Config();}}return _instance;}// 刪除實例static void delInstance(){if(_instance != nullptr) {std::cout << "log: static void delInstance()\n";delete _instance;_instance = nullptr;}}std::string getServerIp() { return _serverIp; }int getServerPort() { return _serverPort; }std::time_t getHotTime() { return _hotTime; }std::string getPathPre() { return _pathPre; }std::string getRarSuf() { return _rarSuf; }std::string getUlPath() { return _ulPath; }std::string getRarPath() { return _rarPath; }std::string getBackups() { return _backups; }
};
Config* Config::_instance = nullptr;
std::mutex Config::_mtx;
}
6.2.2 測試
// 測試Config.hpp
void testConfig()
{ cloud::Config* cof = cloud::Config::getInstance();std::cout << cof->getServerIp() << '\n';std::cout << cof->getServerPort() << '\n';std::cout << cof->getHotTime() << '\n';std::cout << cof->getPathPre() << '\n';std::cout << cof->getRarSuf() << '\n';std::cout << cof->getUlPath() << '\n';std::cout << cof->getRarPath() << '\n';std::cout << cof->getBackups() << '\n';
}
lyf@hcss-ecs-3db9:~/pro/pro24_12_18云備份$ ./cloud
124.70.203.1
9000
30
/listShow/
.lz
./files
./rars
./backups.data