找回密码
 立即注册
首页 业界区 安全 简单文件存储服务 去重存储

简单文件存储服务 去重存储

袁勤 2025-8-17 05:03:07
此篇基于之前写过的一个文件存储的函数,最初版本是只接收数据存储后返回文件id(这个文件id是随机生成的),在写完之后就有考虑加一个去重功能,因为实际用在项目里这代码看来很呆,测试时也看到存储了大量重复文件在系统中.也是最近突然有兴致想到重写一下,当然我不保证这段代码一定正确,同时我的代码也只是理论上的实现,实际使用也不一定适合.如果有问题欢迎您的指正,我先提供修改后的代码,最后再是最初版本.
逻辑简单说就是 获取文件的SHA256值来判断是否已经存储过 ==> 存储文件.
然后实际编写下来就会感受到几种特殊情况:
多个线程同时存储相同文件,检查到没有存储过,都一起存储了一遍,然后u_map也插入key冲突了.
然后就使用到提前存储到u_map中防止另一线程拿到锁发现没存储过也来存储,这时候就又出现新问题那出现存储失败怎么办?另一线程已经判断了存在直接返回了fileId了.
这时候我就再添加一个u_map用于保存文件存储状态,在判断是否已经有存储的时候,再去判断这个存储是否完毕,如果未完毕就等待存储完毕,再返回这里的等待就不考虑重试了.
  1. //最终版本
  2. #include <brpc/server.h>
  3. #include <butil/logging.h>
  4. #include <openssl/sha.h>
  5. //我写的一些库文件就不贴出来献丑了
  6. class FileServiceImpl : public FileService{
  7. public:
  8.     FileServiceImpl(const std::string& base_path)
  9.         :__base_filepath(base_path){
  10.             umask(0);
  11.             mkdir(base_path.c_str(), 0775);
  12.             DEBUG("文件根目录{}",base_path);
  13.             if (__base_filepath.back() != '/') __base_filepath.push_back('/');
  14.         }
  15.     ~FileServiceImpl(){}
  16.     //
  17.     //上传单个文件
  18.     void PutSingleFile(::google::protobuf::RpcController *controller,
  19.                        const ::PutSingleFileReq *request,
  20.                        ::PutSingleFileRsp *response,
  21.                        ::google::protobuf::Closure *done){
  22.         brpc::ClosureGuard guard(done);
  23.         response->set_request_id(request->request_id());
  24.         response->mutable_file_info()->set_file_size(request->file_data().file_size());
  25.         response->mutable_file_info()->set_file_name(request->file_data().file_name());
  26.         StoreResult r = StoreFile(request->file_data().file_content());
  27.         response->set_success(r.success);
  28.         //存储失败
  29.         if(!r.success){
  30.             response->set_errmsg(r.errmsg);
  31.             return;
  32.         }
  33.         response->mutable_file_info()->set_file_id(r.file_id);
  34.         return ;
  35.     }
  36. private:
  37.     /**
  38.      * 用于文件存储状态
  39.      */
  40.     struct FileState
  41.     {
  42.         bool finished = false;
  43.         bool success = false;
  44.         std::string errmsg;
  45.         std::condition_variable cv;
  46.         std::mutex mtx;
  47.     };
  48.     /**
  49.      * 存储返回,用于复用的
  50.      */
  51.     struct StoreResult
  52.     {
  53.         bool success;
  54.         std::string file_id;
  55.         std::string errmsg;
  56.     };
  57.     // 计算文件内容的 SHA256 哈希
  58.     std::string calculateFileHash(const std::string& content) {
  59.         unsigned char hash[SHA256_DIGEST_LENGTH];
  60.         SHA256((const unsigned char*)content.data(), content.size(), hash);
  61.         return std::string(reinterpret_cast<char*>(hash), SHA256_DIGEST_LENGTH);
  62.     }
  63.    
  64.     /**
  65.      * 存储文件,包含哈希去重、并发控制、写入逻辑
  66.      */
  67.     StoreResult StoreFile(const std::string& file_content) {
  68.         StoreResult result;
  69.    
  70.         std::string hash_file = calculateFileHash(file_content);
  71.         std::string file_id = chat_im::util::uuid();
  72.         std::shared_ptr<FileState> file_state;
  73.             bool need_wait = false;
  74.         {
  75.             std::lock_guard lock(__file_mutex);
  76.             auto ite = __file_hashMap.find(hash_file);
  77.             /**
  78.              * 可能已经存储过,检查是否是其他插入key值,但还没存储完毕
  79.              */
  80.             if(ite != __file_hashMap.end()){
  81.                 file_id = ite->second;
  82.                 //判断是否提取插入的值
  83.                 auto s_ite = __writing_files.find(file_id);
  84.                 if(s_ite == __writing_files.end()){
  85.                     /**
  86.                      * 直接返回
  87.                      */
  88.                     result.success = true;
  89.                     result.file_id = file_id;
  90.                     return result;
  91.                 }else{
  92.                     /**
  93.                      * 需要等待
  94.                      */
  95.                     file_state = s_ite->second;
  96.                     need_wait = true;
  97.                 }
  98.             }else{
  99.                 /**
  100.                  * 没有存储过,提前插入map中
  101.                  * 避免多个线程同时进行写入同一文件
  102.                  */
  103.                 __file_hashMap[hash_file] = file_id;
  104.                 file_state = std::make_shared<FileState>();
  105.                 __writing_files[file_id] = file_state;
  106.             }
  107.         }
  108.         /**
  109.          * 等到另一线程写入完毕
  110.          */
  111.         if (need_wait){
  112.             std::unique_lock<std::mutex> lock(file_state->mtx);
  113.             if(!file_state->cv.wait_for(lock,std::chrono::seconds(3),[&](){return file_state->finished;})){
  114.                 // 等待超时处理
  115.                 result.success = false;
  116.                 result.errmsg = "文件处理超时";
  117.                 return result;
  118.             }
  119.             result.success = file_state->success;
  120.             result.errmsg = file_state->errmsg;
  121.             result.file_id = file_id;
  122.             return result;
  123.         }
  124.         /**
  125.          * 存储文件,
  126.          */
  127.         std::string file_name = __base_filepath+file_id;
  128.         bool status = chat_im::util::writeFile(file_name,file_content);
  129.         {
  130.             std::lock_guard lock(file_state->mtx);
  131.             file_state->finished = true;
  132.             file_state->success = status;
  133.             if(!status){
  134.                 file_state->errmsg = "写入文件失败";
  135.             }
  136.         }
  137.         /**
  138.          * 通知等待的线程
  139.          */
  140.         file_state->cv.notify_all();
  141.         
  142.         {
  143.             std::lock_guard lock(__file_mutex);
  144.             __writing_files.erase(file_id);
  145.         }
  146.         if(status == false){
  147.             /**
  148.              * 取消原先插入的map值
  149.              */
  150.             std::lock_guard lock(__file_mutex);
  151.             __file_hashMap.erase(hash_file);
  152.             result.success = false;
  153.             result.errmsg = "写入文件数据失败!";
  154.             return result;
  155.         }
  156.    
  157.         result.success = true;
  158.         result.file_id = file_id;
  159.         return result;
  160.     }
  161. private:
  162.     std::string __base_filepath;
  163.     std::unordered_map<std::string,std::string> __file_hashMap;
  164.     std::unordered_map<std::string, std::shared_ptr<FileState>> __writing_files;
  165.     std::mutex __file_mutex;
  166. };
复制代码
  1. //
  2. //最初版本
  3.     void PutSingleFile(::google::protobuf::RpcController *controller,
  4.                        const ::PutSingleFileReq *request,
  5.                        ::PutSingleFileRsp *response,
  6.                        ::google::protobuf::Closure *done){
  7.         brpc::ClosureGuard guard(done);
  8.         response->set_request_id(request->request_id());
  9.         std::string file_id = chat_im::util::uuid();
  10.         std::string file_name = __base_filepath+file_id;
  11.         bool status = util::writeFile(file_name,request->file_data().file_content());
  12.         if(status == false){
  13.             ERROR("上传文件请求{}:写入失败",request->request_id());
  14.             response->set_success(false);
  15.             response->set_errmsg("写入文件数据失败!");
  16.             return;
  17.         }
  18.         response->set_success(true);
  19.         response->mutable_file_info()->set_file_id(file_id);
  20.         response->mutable_file_info()->set_file_size(request->file_data().file_size());
  21.         response->mutable_file_info()->set_file_name(request->file_data().file_name());
  22.         return ;
  23.     }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册