IM项目-----文件管理子服务

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

文件管理子服务,主要用于管理用户的头像,以及消息中的文件存储,因此需要提供
以下接口:

  1. 文件的上传
    a. 单个文件的上传:这个接口基本用于后台部分,收到文件消息后将文件数据
    转发给文件子服务进行存储
    b. 多个文件的上传:这个接口基本用于后台部分,收到文件消息后将文件数据
    转发给文件子服务进行存储
  2. 文件的下载
    a. 单个文件的下载:在后台用于获取用户头像文件数据,以及客户端用于获取
    文件/语音/图片消息的文件数据
    b. 多个文件的下载:在后台用于大批量获取用户头像数据(比如获取用户列表
    的时候),以及前端的批量文件下载
//获取单个文件请求
message GetSingleFileReq {
    string request_id = 1;
    string file_id = 2;
    optional string user_id = 3;
    optional string session_id = 4;
}

//获取单个文件响应
message GetSingleFileRsp {
    string request_id = 1;
    bool success = 2;
    optional string errmsg = 3;
    optional FileDownloadData file_data = 4;
}

message GetMultiFileReq {
    string request_id = 1;
    optional string user_id = 2;
    optional string session_id = 3;
    repeated string file_id_list = 4;
}

message GetMultiFileRsp {
    string request_id = 1;
    bool success = 2;
    optional string errmsg = 3;
    map<string, FileDownloadData> file_data = 4;
}

message PutSingleFileReq {
    string request_id = 1;
    optional string user_id = 2;
    optional string session_id = 3;
    FileUploadData file_data = 4;
}

message PutSingleFileRsp {
    string request_id = 1;
    bool success = 2;
    optional string errmsg = 3;
    optional FileMessageInfo file_info = 4;
}

message PutMultiFileReq {
    string request_id = 1;
    optional string user_id = 2;
    optional string session_id = 3;
    repeated FileUploadData file_data = 4;
}

message PutMultiFileRsp {
    string request_id = 1;
    bool success = 2;
    string errmsg = 3;
    repeated FileMessageInfo file_info = 4;
}

rpc服务
service FileService {
    rpc GetSingleFile(GetSingleFileReq) returns (GetSingleFileRsp);
    rpc GetMultiFile(GetMultiFileReq) returns (GetMultiFileRsp);
    rpc PutSingleFile(PutSingleFileReq) returns (PutSingleFileRsp);
    rpc PutMultiFile(PutMultiFileReq) returns (PutMultiFileRsp);
}

一、搭建思想

文件管理子服务和语音管理子服务基本一致,他们都有服务注册,rpc服务器.
唯一不同的是文件管理子服务中的rpc服务是进行文件的读写。
我们通过标准库的文件读写类进行一个文件的读写。
针对文件的上传,我们需要给文件生成一个uuid,这个uuid就作为文件的名称在服务器进行文件的写入。然后在resp中设置进生成的uuid。
针对文件的下载,调用端只需要提供一个文件id,也就是我们生成的uuid,服务端就可以找到对应的文件了。

在这里插入图片描述

二、服务器搭建

1.继承fileService类,重写业务方法

文件管理子服务提供了四个功能。单个文件的上传下载,多个文件的上传下载。
针对上传:首先为该文件生成一个uuid,然后进行进行文件内容的写入。最后返回文件Id(uuid)。
针对下载:首先获取文件ID,然后找到文件进行文件内容的读取,最后返回文件内容。

只有一个地方需要注意,就是该类有一个成员变量std::string _storage_path。
我们的文件都是存贮在这个路径下的,所以当我们进行文件的写入和读取时,都需要先在文件id的前面拼接上这个路径。

//1.继承文件服务类,重写业务方法
    class FileServiceImpl : public FileService
    {
    public:
        FileServiceImpl(const std::string& path)
            :_storage_path(path)
        {
            umask(0);
            if(_storage_path.back() != '/') _storage_path += "/";
            mkdir(_storage_path.c_str(), 0775);
        }
        ~FileServiceImpl(){}

        //获取单个文件
        void GetSingleFile(google::protobuf::RpcController* controller,
                        const ::lkm_im::GetSingleFileReq* request,
                        ::lkm_im::GetSingleFileRsp* response,
                        ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);
            response->set_request_id(request->request_id());

            //获取文件ID 
            std::string file_id = request->file_id();
            std::string file_name = _storage_path + file_id;
            //读取文件内容
            std::string body;
            bool ret = readFile(file_name,body);
            if(!ret){
                LOG_ERROR("{},获取单个文件失败",request->request_id());
                response->set_success(false);
                response->set_errmsg("读取文件数据失败");
                return;
            }
            //组织响应
            response->set_success(true);
            response->mutable_file_data()->set_file_id(request->file_id());
            response->mutable_file_data()->set_file_content(body);
        }

        //获取多个文件
        void GetMultiFile(google::protobuf::RpcController* controller,
                        const ::lkm_im::GetMultiFileReq* request,
                        ::lkm_im::GetMultiFileRsp* response,
                        ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);
            response->set_request_id(request->request_id());

            //循环处理多个文件
            for (int i = 0; i < request->file_id_list_size(); i++)
            {
                //获取文件ID (文件名)
                std::string file_id = request->file_id_list(i);
                std::string file_name = _storage_path + file_id;
                //读取文件内容
                std::string body;
                bool ret = readFile(file_name,body);
                if(!ret){
                    LOG_ERROR("{},获取多个文件失败",request->request_id());
                    response->set_success(false);
                    response->set_errmsg("读取文件数据失败");
                    return;
                }

                //组织响应
                FileDownloadData fileDownloadData;
                fileDownloadData.set_file_id(file_id);
                fileDownloadData.set_file_content(body);
                response->mutable_file_data()->insert({file_id,fileDownloadData});
            }
            
            response->set_success(true);
        }

        //上传单个文件
        void PutSingleFile(google::protobuf::RpcController* controller,
                        const ::lkm_im::PutSingleFileReq* request,
                        ::lkm_im::PutSingleFileRsp* response,
                        ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);
            response->set_request_id(request->request_id());

            //为文件生成一个uuid
            std::string fid = uuid();
            std::string file_name = _storage_path + fid;    //文件实际存储路径
            //获取文件内容
            std::string file_content = request->file_data().file_content();
            //将内容写入文件
            int ret = writeFile(file_name,file_content);
            if(!ret){
                LOG_ERROR("{},上传单个文件失败",request->request_id());
                response->set_success(false);
                response->set_errmsg("写入文件数据失败");
                return;
            }
            
            //组织响应  给客户端返回我们生成的文件ID就行
            response->mutable_file_info()->set_file_id(fid);
            response->mutable_file_info()->set_file_size(request->file_data().file_size());
            response->mutable_file_info()->set_file_name(request->file_data().file_name());
            response->set_success(true);
        }

        //上传多个文件
        void PutMultiFile(google::protobuf::RpcController* controller,
                        const ::lkm_im::PutMultiFileReq* request,
                        ::lkm_im::PutMultiFileRsp* response,
                        ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);
            response->set_request_id(request->request_id());

            for(int i = 0; i < request->file_data_size(); i++)
            {
                //为文件生成一个uuid
                std::string fid = uuid();
                std::string file_name = _storage_path + fid;

                //获取文件内容
                std::string file_content = request->file_data(i).file_content();

                //将内容写入文件
                int ret = writeFile(file_name,file_content);
                if(!ret){
                    LOG_ERROR("{},上传多个文件失败",request->request_id());
                    response->set_success(false);
                    response->set_errmsg("写入文件数据失败");
                    return;
                }
                
                //组织响应  给客户端返回我们生成的文件ID就行
                lkm_im::FileMessageInfo * fileMessageInfo = response->add_file_info();
                fileMessageInfo->set_file_id(fid);
                fileMessageInfo->set_file_name(request->file_data(i).file_name());
                fileMessageInfo->set_file_size(request->file_data(i).file_size());
                
            }
            response->set_success(true);
        }
    private:
        std::string _storage_path;   //存储文件路径
    };

服务器类设计

和语音识别子服务一样,这里也采用了建造者模式。但是我们的文件管理子服务只有两个对象,一个rpc服务器,一个服务注册对象。

//2.文件存储管理子服务服务器编写
    class FileServer
    {
    public:
    using ptr = std::shared_ptr<FileServer>;
        FileServer(const Registry::ptr& registry,const std::shared_ptr<brpc::Server>& server)
            :_registry(registry),_server(server)
        {

        }

        ~FileServer(){}

        //启动rpc服务器
        void start()
        {
            _server->RunUntilAskedToQuit();
        }

    private:
        Registry::ptr _registry;    //服务注册对象
        std::shared_ptr<brpc::Server> _server;  //rpc服务器
    };

建造者类

在构造rpc服务器时需要传入一个路径,因为这里需要为rpc服务器添加服务,需要构造fileServiceImpl对象,这个对象需要传入一个path存储路径。

//3.文件存储管理子服务构造者
    class FileServerBuild
    {
    public:
        void make_registry(const std::string& etcd_host,const std::string& service_name,const std::string& service_host)
        {
            _registry = std::make_shared<Registry>(etcd_host);
            //进行服务注册
            _registry->registry(service_name,service_host);
        }

        void make_brpc(uint16_t port,uint32_t timeout = -1,uint8_t num_threads = 1,const std::string& path = "./data/")
        {
            //创建brpc服务器对象
            _server = std::make_shared<brpc::Server>();
            //添加服务
            FileServiceImpl *FileService = new FileServiceImpl(path);    //把这个对象的交给_server释放
            int ret = _server->AddService(FileService,brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
            if (ret == -1) {
                LOG_ERROR("添加rpc服务失败");
                abort();
            }

            brpc::ServerOptions options;
            options.idle_timeout_sec = timeout;
            options.num_threads = num_threads;
            ret = _server->Start(port,&options);
            if(ret == -1){
                LOG_ERROR("rpc服务器启动失败");
                abort();
            }
        }


        FileServer::ptr build()
        {
            if(!_registry){
                LOG_ERROR("服务注册客户端对象未构造");
                abort();
            }
            if(!_server){
                LOG_ERROR("rpc服务器对象未构造");
                abort();
            }

            FileServer::ptr FileServer(new lkm_im::FileServer(_registry,_server));
            return FileServer;
        }
    private:
        Registry::ptr _registry;    //服务注册对象
        std::shared_ptr<brpc::Server> _server;  //rpc服务器
    };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值