文章目录
- util.hpp
- config.hpp
- hot.hpp
- data.hpp
- server.hpp
- server.cc
- Makefile
- cloud.conf
util.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "bundle.h"
namespace cloud
{
namespace fs = std::experimental::filesystem;
class fileUtil
{
public:
fileUtil(std::string filename)
:_filename(filename)
{}
bool Remove()
{
if(exists() == false)
{
return true;
}
remove(_filename.c_str());
return true;
}
size_t fileSize()
{
struct stat st;
int ret = stat(_filename.c_str(), &st);
if(ret == -1)
{
std::cout << strerror(errno) << std::endl;
return 0;
}
return st.st_size;
}
time_t lastModifyTime()
{
struct stat st;
int ret = stat(_filename.c_str(), &st);
if(ret == -1)
{
std::cout << strerror(errno) << std::endl;
return -1;
}
return st.st_mtime;
}
time_t lastAccessTime()
{
struct stat st;
int ret = stat(_filename.c_str(), &st);
if(ret == -1)
{
std::cout << strerror(errno) << 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 setContent(const std::string &body)
{
std::ofstream ofs;
ofs.open(_filename, std::ios::binary);
if(ofs.is_open() == false)
{
std::cout << "setContent open failed
";
return false;
}
ofs.write(&body[0], body.size());
if(ofs.good() == false)
{
std::cout << "setContent write failed
";
ofs.close();
return false;
}
ofs.close();
return true;
}
bool getPosLen(std::string *body, size_t pos, size_t len)
{
std::ifstream ifs;
if(pos + len > fileSize())
{
std::cout << "getPosLen failed
";
return false;
}
ifs.open(_filename, std::ios::binary);
if(ifs.is_open() == false)
{
std::cout << "getPosLen open failed
";
return false;
}
body->resize(len - pos);
ifs.read(&(*body)[0], len);
if(ifs.good() == false)
{
std::cout << "getPosLen read failed
";
ifs.close();
return false;
}
ifs.close();
return true;
}
bool getContent(std::string *body)
{
size_t n = fileSize();
return getPosLen(body, 0, n);
}
bool exists()
{
return fs::exists(_filename);
}
bool createDirectory()
{
if(exists())
return true;
return fs::create_directories(_filename);
}
bool getDirectory(std::vector<std::string> *arry)
{
for(const fs::directory_entry& entry : fs::directory_iterator{_filename})
{
if(fs::is_directory(entry))
continue;
arry->push_back(fs::path(entry).relative_path().string());
}
return true;
}
bool compress(const std::string &packname)
{
std::string body;
getContent(&body);
std::string buffer = bundle::pack(bundle::LZIP, body);
std::ofstream ofs;
ofs.open(packname, std::ios::binary);
if(ofs.is_open() == false)
{
std::cout << "compress open failed
";
return false;
}
ofs.write(&buffer[0], buffer.size());
if(ofs.good() == false)
{
std::cout << "compress write failed
";
ofs.close();
return false;
}
ofs.close();
return true;
}
bool uncompress(const std::string &filename)
{
std::string body;
getContent(&body);
std::string buffer = bundle::unpack(body);
std::ofstream ofs;
ofs.open(filename, std::ios::binary);
if(ofs.is_open() == false)
{
std::cout << "uncompress open failed
";
return false;
}
ofs.write(&buffer[0], buffer.size());
if(ofs.good() == false)
{
std::cout << "uncompress write failed
";
ofs.close();
return false;
}
ofs.close();
return true;
}
private:
std::string _filename;
};
class JsonUtil
{
public:
static bool Serialize(const Json::Value &root, std::string *str)
{
Json::StreamWriterBuilder swb;
std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
std::stringstream ss;
int ret = sw->write(root, &ss);
if(ret != 0)
{
std::cout << "Serialize failed" << std::endl;
return false;
}
*str = ss.str();
return true;
}
static bool UnSerialize(const std::string &str, Json::Value *root)
{
Json::CharReaderBuilder crb;
std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
std::string errs;
bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &errs);
if(ret == false)
{
std::cout << "UnSerialize failed " << errs << std::endl;
return false;
}
return true;
}
};
}
config.hpp
#pragma once
#include
#include "util.hpp"
namespace cloud
{
#define CONFIG_FILE "./cloud.conf"
class Config
{
private:
Config()
{
ReadConfigFile();
}
bool ReadConfigFile()
{
fileUtil fu(CONFIG_FILE);
std::string body;
bool ret = fu.getContent(&body);
if(ret == false)
{
std::cout << "ReadConfigFile getContent faile" << std::endl;
}
Json::Value root;
ret = cloud::JsonUtil::UnSerialize(body, &root);
if(ret == false)
{
std::cout << "ReadConfigFile UnSerialize faile" << std::endl;
}
_hot_time = root["hot_time"].asInt();
_server_port = root["server_port"].asInt();
_server_ip = root["server_ip"].asString();
_download_prefix = root["download_prefix"].asString();
_packfile_suffix = root["packfile_suffix"].asString();
_pack_dir = root["pack_dir"].asString();
_back_dir = root["back_dir"].asString();
_backup_file = root["backup_file"].asString();
}
public:
static Config* getIstance()
{
if(_instance == nullptr)
{
_mtx.lock();
if(_instance == nullptr)
{
_instance = new Config();
}
_mtx.unlock();
}
return _instance;
}
int getHotTime()
{
return _hot_time;
}
int getServerPort()
{
return _server_port;
}
std::string getServerIp()
{
return _server_ip;
}
std::string getDownloadPrefix()
{
return _download_prefix;
}
std::string getPackfileSuffix()
{
return _packfile_suffix;
}
std::string getPackDir()
{
return _pack_dir;
}
std::string getBackDir()
{
return _back_dir;
}
std::string getBackupFile()
{
return _backup_file;
}
private:
static Config* _instance;
static std::mutex _mtx;
private:
int _hot_time;
int _server_port;
std::string _server_ip;
std::string _download_prefix;
std::string _packfile_suffix;
std::string _pack_dir;
std::string _back_dir;
std::string _backup_file;
};
Config* Config::_instance = nullptr;
std::mutex Config::_mtx;
}
hot.hpp
#pragma once
#include
#include
#include "data.hpp"
extern cloud::dataManager *_data;
namespace cloud
{
class HotManager
{
private:
bool hotJuge(const std::string& filename)
{
fileUtil fu(filename);
time_t last_atime = fu.lastAccessTime();
time_t cur_time = time(nullptr);
if(cur_time - last_atime > _hot_time)
{
return false;
}
return true;
}
public:
HotManager()
{
Config* f = Config::getIstance();
_back_dir = f->getBackDir();
_pack_dir = f->getPackDir();
_packfile_suffix = f->getPackfileSuffix();
_hot_time = f->getHotTime();
fileUtil fu1(_back_dir);
fileUtil fu2(_pack_dir);
fu1.createDirectory();
fu2.createDirectory();
}
bool runMoudle()
{
while(true)
{
fileUtil fu(_back_dir);
std::vector<std::string> arry;
fu.getDirectory(&arry);
for(auto& e : arry)
{
if(hotJuge(e))
{
continue;
}
BackupInfo info;
bool ret = _data->getBifoByRealPath(e, &info);
if(ret == false)
{
std::cout << "runMoudle faile" << std::endl;
info.NewBackupInfo(e);
}
fileUtil fu(e);
fu.compress(info.pack_path);
fu.Remove();
info.pack_flag = true;
_data->update(info);
}
usleep(1000);
}
return true;
}
private:
std::string _back_dir;
std::string _pack_dir;
std::string _packfile_suffix;
int _hot_time;
};
}
data.hpp
#pragma once
#include
#include
#include "util.hpp"
#include "config.hpp"
namespace cloud
{
struct BackupInfo
{
bool pack_flag;
size_t file_size;
time_t modify_time;
time_t access_time;
std::string real_path;
std::string pack_path;
std::string url;
bool NewBackupInfo(const std::string& filepath)
{
fileUtil fu(filepath);
if(fu.exists() == false)
{
std::cout << "NewBackupInfo fail" << std::endl;
return false;
}
pack_flag = false;
file_size = fu.fileSize();
modify_time = fu.lastModifyTime();
access_time = fu.lastAccessTime();
real_path = filepath;
Config* f = Config::getIstance();
std::string packdir = f->getPackDir();
std::string packfile_suffix = f->getPackfileSuffix();
pack_path = packdir + fu.fileName() + packfile_suffix;
std::string download_prefix = f->getDownloadPrefix();
url = download_prefix + fu.fileName();
return true;
}
};
class dataManager
{
public:
dataManager()
{
_backup_file = Config::getIstance()->getBackupFile();
pthread_rwlock_init(&_rwlock, nullptr);
initLoad();
}
bool initLoad()
{
fileUtil fu(_backup_file);
if(fu.exists() == false)
{
return true;
}
std::string body;
bool ret = fu.getContent(&body);
if(ret == false)
{
std::cout << "InitLoad getContent failed" << std::endl;
return false;
}
Json::Value root;
ret = JsonUtil::UnSerialize(body, &root);
if(ret == false)
{
std::cout << "InitLoad getContent failed" << std::endl;
return false;
}
for(int i = 0; i < root.size(); i++)
{
BackupInfo info;
info.pack_flag = root[i]["pack_flag"].asBool();
info.file_size = root[i]["file_size"].asInt64();
info.modify_time = root[i]["modify_time"].asInt64();
info.access_time = root[i]["access_time"].asInt64();
info.real_path = root[i]["real_path"].asString();
info.pack_path = root[i]["pack_path"].asString();
info.url = root[i]["url"].asString();
insert(info);
}
return true;
}
bool storage()
{
Json::Value root;
for(auto& e : _table)
{
Json::Value tmp;
tmp["pack_flag"] = e.second.pack_flag;
tmp["file_size"] = (Json::Int64)e.second.file_size;
tmp["modify_time"] = (Json::Int64)e.second.modify_time;
tmp["access_time"] = (Json::Int64)e.second.access_time;
tmp["real_path"] = e.second.real_path;
tmp["pack_path"] = e.second.pack_path;
tmp["url"] = e.second.url;
root.append(tmp);
}
std::string body;
bool ret = JsonUtil::Serialize(root, &body);
if(ret == false)
{
std::cout << "Storage Serialize faile" << std::endl;
return false;
}
fileUtil fu(_backup_file);
ret = fu.setContent(body);
if(ret == false)
{
std::cout << "Storage setContent faile" << std::endl;
return false;
}
return true;
}
bool insert(const BackupInfo& Info)
{
pthread_rwlock_wrlock(&_rwlock);
_table[Info.url] = Info;
pthread_rwlock_unlock(&_rwlock);
storage();
return true;
}
bool update(const BackupInfo& Info)
{
pthread_rwlock_wrlock(&_rwlock);
_table[Info.url] = Info;
pthread_rwlock_unlock(&_rwlock);
storage();
return true;
}
bool getBifoByUrl(const std::string& url, BackupInfo* Info)
{
pthread_rwlock_wrlock(&_rwlock);
auto ret = _table.find(url);
if(ret == _table.end())
{
pthread_rwlock_unlock(&_rwlock);
return false;
}
*Info = ret->second;
pthread_rwlock_unlock(&_rwlock);
return true;
}
bool getBifoByRealPath(const std::string& realPath, BackupInfo* Info)
{
pthread_rwlock_wrlock(&_rwlock);
for(auto& e : _table)
{
if(e.second.real_path == realPath)
{
*Info = e.second;
pthread_rwlock_unlock(&_rwlock);
return true;
}
}
pthread_rwlock_unlock(&_rwlock);
return false;
}
bool getAll(std::vector<BackupInfo> *arry)
{
pthread_rwlock_wrlock(&_rwlock);
for(auto& e : _table)
{
arry->push_back(e.second);
}
pthread_rwlock_unlock(&_rwlock);
return true;
}
~dataManager()
{
pthread_rwlock_destroy(&_rwlock);
}
private:
std::string _backup_file;
pthread_rwlock_t _rwlock;
std::unordered_map<std::string, BackupInfo> _table;
};
}
server.hpp
#pragma once
#include "data.hpp"
#include "httplib.h"
extern cloud::dataManager *_data;
namespace cloud
{
class serevr
{
private:
static void upLoad(const httplib::Request& rq, const httplib::Response& rp)
{
bool ret = rq.has_file("file");
if(ret == false)
{
return ;
}
const auto& file = rq.get_file_value("file");
std::string real_path = _back_dir + fileUtil(file.filename).fileName();
fileUtil fu(real_path);
fu.setContent(file.content);
BackupInfo info;
info.NewBackupInfo(real_path);
_data->insert(info);
return;
}
static std::string timeToString(time_t t)
{
return std::ctime(&t);
}
static void listShow(const httplib::Request& rq, httplib::Response& rp)
{
std::vector<BackupInfo> arry;
_data->getAll(&arry);
std::stringstream ss;
ss << "Download";
ss << " Download
";
for(auto& e : arry)
{
ss << "";
std::string filename = fileUtil(e.real_path).fileName();
ss << " << e.url << "'>" << filename << " | ";
ss << "";
ss << timeToString(e.modify_time);
ss << " | ";
ss << "";
ss << e.file_size / 1024 << "K";
ss << " | ";
ss << "
";
}
ss << "
";
rp.body = ss.str();
rp.set_header("Content-Type", "text/html");
rp.status = 200;
}
static std::string getETagInfo(const BackupInfo& info)
{
std::string etag;
etag += fileUtil(info.real_path).fileName();
etag += "-";
etag += std::to_string(info.file_size);
etag += "-";
etag += std::to_string(info.modify_time);
return etag;
}
static void downLoad(const httplib::Request& rq, httplib::Response& rp)
{
std::string url = rq.path;
BackupInfo info;
_data->getBifoByUrl(url, &info);
if(info.pack_flag == true)
{
fileUtil fu(info.pack_path);
fu.uncompress(info.real_path);
fu.Remove();
info.pack_flag = false;
_data->insert(info);
}
fileUtil fu(info.real_path);
fu.getContent(&rp.body);
rp.set_header("Accept-Ranges", "bytes");
rp.set_header("ETag", getETagInfo(info));
rp.set_header("Content-Type", "application/octet-stream");
if(rq.has_header("If-Range") && rq.get_header_value("If-Range") == getETagInfo(info))
{
rp.status = 206;
}
else
{
rp.status = 200;
}
}
public:
serevr()
{
Config* cnf = Config::getIstance();
_server_port = cnf->getServerPort();
_server_ip = cnf->getServerIp();
_download_prefix = cnf->getDownloadPrefix();
_back_dir = cnf->getBackDir();
}
bool RunModule()
{
_server.Post("/upload",upLoad);
_server.Get("/listshow", listShow);
_server.Get("/", listShow);
std::string url = _download_prefix + "(.*)";
_server.Get(url,downLoad);
_server.listen("0.0.0.0", _server_port);
return true;
}
private:
int _server_port;
std::string _server_ip;
std::string _download_prefix;
static std::string _back_dir;
httplib::Server _server;
};
std::string serevr::_back_dir;
}
server.cc
#include "util.hpp"
#include "config.hpp"
#include "data.hpp"
#include "hot.hpp"
#include "server.hpp"
#include
cloud::dataManager *_data;
void server()
{
cloud::serevr s;
s.RunModule();
}
void hot()
{
cloud::HotManager h;
h.runMoudle();
}
int main(int argc, char *argv[])
{
_data = new cloud::dataManager();
std::thread thread_hot(hot);
std::thread thread_server(server);
thread_hot.join();
thread_server.join();
return 0;
}
Makefile
.PHONY:util
cloudServer:cloudServer.cc
g++ -o $@ $^ -L./lib -lpthread -lstdc++fs -ljsoncpp -lbundle
cloud.conf
{
"hot_time" : 30,
"server_port" : 8080,
"server_ip" : "-.-.-.-",
"download_prefix" : "/download/",
"packfile_suffix" : ".lz",
"pack_dir" : "./packdir/",
"back_dir" : "./backdir/",
"backup_file" : "./cloud.dat"
}