文章目录 util.hpp config.hpp hot.hpp data.hpp server.hpp server.cc Makefile cloud.conf
util.hpp
# pragma once
# include <iostream>
# include <fstream>
# include <string>
# include <vector>
# include <sys/stat.h>
# include <unistd.h>
# include <cstring>
# include <cstdint>
# include <experimental/filesystem>
# include <jsoncpp/json/json.h>
# include <memory>
# 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\n" ; return false ; } ofs. write ( & body[ 0 ] , body. size ( ) ) ; if ( ofs. good ( ) == false ) { std:: cout << "setContent write failed\n" ; 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\n" ; return false ; } ifs. open ( _filename, std:: ios:: binary) ; if ( ifs. is_open ( ) == false ) { std:: cout << "getPosLen open failed\n" ; return false ; } body-> resize ( len - pos) ; ifs. read ( & ( * body) [ 0 ] , len) ; if ( ifs. good ( ) == false ) { std:: cout << "getPosLen read failed\n" ; 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\n" ; return false ; } ofs. write ( & buffer[ 0 ] , buffer. size ( ) ) ; if ( ofs. good ( ) == false ) { std:: cout << "compress write failed\n" ; 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\n" ; return false ; } ofs. write ( & buffer[ 0 ] , buffer. size ( ) ) ; if ( ofs. good ( ) == false ) { std:: cout << "uncompress write failed\n" ; 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 <mutex>
# 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 <cstdio>
# include <unistd.h>
# 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 <unordered_map>
# include <pthread.h>
# 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 << "<html><head><title>Download</title></head>" ; ss << " <body><h1>Download</h1><table>" ; for ( auto & e : arry) { ss << "<tr>" ; std:: string filename = fileUtil ( e. real_path) . fileName ( ) ; ss << "<td><a href='" << e. url << "'>" << filename << "</a></td>" ; ss << "<td align='right'>" ; ss << timeToString ( e. modify_time) ; ss << "</td>" ; ss << "<td align='right'>" ; ss << e. file_size / 1024 << "K" ; ss << "</td>" ; ss << "</tr>" ; } ss << "</table></body></html>" ; 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 <thread> 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. ccg++ - 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"
}