微服务即时通信系统---(七)文件管理子服务

news2025/4/3 22:25:04

目录

功能设计

模块划分

业务接口/功能示意图

服务实现流程

服务代码实现

封装文件操作模块(utils.hpp)

获取唯一标识ID

文件读操作

文件写操作

编写proto文件

文件元信息

文件管理proto

单文件上传

多文件上传

单文件下载

多文件下载

RPC调用

服务端创建子类(FileManageServiceImpl)完成RPC服务调用函数重写

SingleFileUp(单文件上传)

MultiFileUp(多文件上传)

SingleFileDown(单文件下载)

MultiFileDown(多文件下载)

RPC服务端代码(总)

服务端完成文件管理子服务类(FileManageServer)

注意

实例化服务类对象,启动服务

工程系统构建配置文件(CMakeLists.txt)

服务测试


本章节,主要对项目中文件管理子服务模块进行分析、开发与测试。

功能设计

文件管理子服务,主要提供两个功能:文件的上传和文件的下载,因此,文件管理子服务主要提供4个功能性接口:

1、单个文件的上传:主要用于后台,将收到的文件消息进行存储。

2、多个文件的上传:主要用于后台,将收到的文件消息进行存储。

3、单个文件的下载:在后台用于获取头像文件数据,以及客户端用于获取文件数。

4、多个文件的下载:在后台用于大批量获取头像文件数据,以及前端的批量文件下载。

模块划分

参数/配置文件解析模块 基于gflags框架直接使用,进行参数/配置文件的解析。
日志模块 基于spdlog封装的logger 直接进行日志输出。
服务注册模块 基于etcd框架封装的注册模块 直接进行文件管理子服务模块的服务注册。
RPC服务模块 基于brpc框架 搭建文件管理子服务的RPC服务器。
文件操作模块 基于标准库的文件流操作实现文件的读写封装,用于文件操作。

业务接口/功能示意图

文件上传:

文件下载/获取:

服务实现流程

1、实现文件操作模块的封装(utils.hpp),其中包括 文件读操作、文件写操作,外加一个获取唯一标识ID的操作(用于用户ID、文件ID等)。
2、编写服务所需的proto文件,利用protoc工具生成RPC服务器所需的.pb.h 和 .pb.cc 项目文件。
3、服务端 创建子类,继承于proto文件中RPC调用类,并进行功能性接口函数重写。
4、服务端 完成文件管理子服务类。
5、实例化 服务类对象,启动服务。

服务代码实现

封装文件操作模块(utils.hpp)

获取唯一标识ID

在代码中,文件ID、用户ID 或者是 会话ID 都由此处操作来获取。

这里使用16个随机的字符串 组成这个唯一的标识ID。

实现思想:

1、先生成6个 0 ~ 255 内的随机数字,而1 个 字节,为 8位。再将这8位,分成4 4 位,每4位转换成1个16进制数字,从而 1个随机数字 转换成 2个 16位数字。至此,得到12 位 随机16进制字符。

2、再通过一个 静态变量,生成一个2 字节的 编号数字,同样 转换成 4 个 16位数字。至此,得到4位 随机16进制字符。

3、将1和2进行拼接,得到16个随机的字符串。

utils.hpp:

    // 生成一个唯一标识ID
    std::string uuid()
    {
        // 1. 生成12位16进制字符
        std::random_device rd;                                   // 实例化设备随机数对象, 用于生成设备随机数(唯一性更强)
        std::mt19937 generator(rd());                            // 以设备随机数为种子, 实例化随机数对象(mt19937:一种生成随机数的方式)
        std::uniform_int_distribution<int> distribution(0, 255); // 限定生成随机数的范围

        std::stringstream ss;
        for (int i = 0; i < 6; ++i)
        {
            if (i == 2)
                ss << "-"; // 添加-, 最终形式为: xxxx-yyyy-zzzz-dddd
            ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
            // distribution(generator) : 生成一个 0-255的随机数
            // std::hex + std::setw(2) : 转换为 2个 16进制数
            // std::setfill('0'): 不足的,前面用0填充
        }

        // 2. 通过静态变量生成 4位 16进制字符
        ss << "-";
        static std::atomic<short> idx(0);
        short tmp = idx.fetch_add(1);
        ss << std::setw(4) << std::setfill('0') << std::hex << tmp;
        return ss.str();
    }

文件读操作

通过传入文件名 和 承接文件内容的string,用来获取文件内容。

实现思想:

1、根据文件名打开文件。

2、跳转文件内部指针,获取文件指针偏移量(文件大小)。

3、再将文件内部指针跳转开头,进行读取文件内容。

4、关闭文件。

utils.hpp:

    // 读取文件
    bool ReadFile(const std::string &file_name, std::string &body)
    {
        std::ifstream ifs(file_name, std::ios::in | std::ios::binary);
        if (ifs.is_open() == false)
        {
            LOG_ERROR("打开文件失败, file_name: {}", file_name);
            return false;
        }

        ifs.seekg(0, std::ios::end);
        size_t file_size = ifs.tellg();
        ifs.seekg(0, std::ios::beg);
        body.resize(file_size);
        ifs.read(&body[0], file_size);

        if (ifs.good() == false)
        {
            LOG_ERROR("读取文件失败, file_name: {}", file_name);
            ifs.close();
            return false;
        }

        ifs.close();
        return true;
    }

文件写操作

通过传入文件名 和 想要写入的内容,用来向文件写入数据。

实现思想:

1、根据文件名打开文件。

2、写入数据。

3、关闭文件。

utils.hpp:

    // 写入文件
    bool WriteFile(const std::string &file_name, const std::string &body)
    {
        std::ofstream ofs(file_name, std::ios::out | std::ios::binary | std::ios::trunc); // 覆盖式写入
        if (ofs.is_open() == false)
        {
            LOG_ERROR("打开文件失败, file_name: {}", file_name);
            return false;
        }

        ofs.write(body.c_str(), body.size());
        if (ofs.good() == false)
        {
            LOG_ERROR("写入文件失败, file_name: {}", file_name);
            ofs.close();
            return false;
        }

        ofs.close();
        return true;
    }

编写proto文件

文件元信息

首先对于文件来说,不光需要编写文件的上传/下载的proto文件,文件还需要有它的元信息(文件ID、文件名称、文件大小、文件内容),并且后续用户发送的消息里面,也可能是文件,需要我们进行识别,所以将文件的元信息,单独放在一个proto文件里面(后续用户元信息、会话元信息、图像元信息、语音元信息、字符串消息元信息都放在里面)。统称为 base.proto

文件元信息(FileInfo)成员:

1、file_id:文件ID。

2、file_size:文件大小。

3、file_name:文件名称。

4、file_content:文件内容。

// ------文件元信息------
message FileInfo
{
    optional string file_id = 1;
    optional int64 file_size = 2;
    optional string file_name = 3;
    optional bytes file_content = 4;
};

考虑到多文件上传/下载需要repeated的相同信息,所以将文件的上传和下载所需要的信息也放进来。 

// ------文件元信息 + 文件上传/下载信息------
message FileInfo
{
    optional string file_id = 1;
    optional int64 file_size = 2;
    optional string file_name = 3;
    optional bytes file_content = 4;
};
message FileUpInfo
{
    string file_name = 1;
    int64 file_size = 2;
    bytes file_content = 3;
};
message FileDownInfo
{
    string file_id = 1;
    bytes file_content = 2;
};

文件管理proto

既然文件管理模块有4个功能性接口,那么就有4个对应的请求与响应结构,以及最终的PRC调用(fileManage.proto)。

单文件上传

SingleFileUpReq包含成员:

1、请求ID:标识请求的唯一性。

2、文件上传信息:存储文件上传所需信息(文件名、文件大小、文件内容)。

3、用户ID(optional):标明来自哪个用户。

4、会话ID(optional):标明来自哪个会话。

SingleFileUpResp包含成员:

1、请求ID:对应请求中的请求ID,标识请求唯一性。

2、成功标识:标识该次请求的处理结果。

3、错误信息(optional):如果处理出错,记录出错信息。

4、文件元信息:存储文件元信息(文件ID、文件大小、文件名、文件内容)。

// ------单文件上传------
message SingleFileUpReq
{
    string req_id = 1;
    FileUpInfo file_up_info = 2;
    optional string user_id = 3;
    optional string session_id = 4;
};
message SingleFileUpResp
{
    string req_id = 1;
    bool success = 2;
    optional string err_msg = 3;
    optional FileInfo file_info = 4;
};
多文件上传

多文件上传和单文件上传没啥不同的,就是里面的文件东西,由列表来构成。

// ------多文件上传------
message MultiFileUpReq
{
    string req_id = 1;
    repeated FileUpInfo file_up_info_list = 2;
    optional string user_id = 3;
    optional string session_id = 4;
};
message MultiFileUpResp
{
    string req_id = 1;
    bool success = 2;
    optional string err_msg = 3;
    repeated FileInfo file_info_list = 4;
};
单文件下载

SingleFileDownReq包含成员:

1、请求ID:标识请求的唯一性。

2、文件ID:根据文件ID才能找到文件。

3、用户ID(optional):标明来自哪个用户。

4、会话ID(optional):标明来自哪个会话。

SingleFileDownResp包含成员:

1、请求ID:对应请求中的请求ID,标识请求唯一性。

2、成功标识:标识该次请求的处理结果。

3、错误信息(optional):如果处理出错,记录出错信息。

4、文件下载信息:存储文

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2308459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mosfet的驱动设计-开关损耗

目录 1.开关时的DS损耗 2.导通损耗 3.截止损耗 4&#xff0e;驱动损耗 mos管的损耗主要有开关损耗和导通损耗两部分&#xff0c;开关损耗包括mos管开通是消耗的能量和在mos在线性区产生的损耗。导通损耗是由mos的导通电阻电阻消耗的能量。 mos的实际模型 我们先来感性的…

萌新学 Python 之 with 文件操作语句

with 语句用于资源管理&#xff0c;避免资源泄露&#xff0c;对文件操作时&#xff0c;不管文件是否有异常&#xff0c;都会自动清理和关闭 with 语句的格式&#xff1a; with open(文件路径, mode模式, encodingutf-8) as file_obj: # as 取别名print(对文件进行操作&…

C# Unity 唐老狮 No.2 模拟面试题

本文章不作任何商业用途 仅作学习与交流 安利唐老狮与其他老师合作的网站,内有大量免费资源和优质付费资源,我入门就是看唐老师的课程 打好坚实的基础非常非常重要: Unity课程 - 游习堂 - 唐老狮创立的游戏开发在线学习平台 - Powered By EduSoho 如果你发现了文章内特殊的字体…

FFmpeg-chapter3-读取视频流(原理篇)

ffmpeg网站&#xff1a;About FFmpeg 1 库介绍 &#xff08;1&#xff09;libavutil是一个包含简化编程函数的库&#xff0c;包括随机数生成器、数据结构、数学例程、核心多媒体实用程序等等。 &#xff08;2&#xff09;libavcodec是一个包含音频/视频编解码器的解码器和编…

单片机中的flah和RAM

片机的 Flash 和 RAM 是两种关键的内存类型&#xff0c;分别用于存储程序代码和运行时数据。 Flash 存储器 用途&#xff1a;用于存储程序代码&#xff08;如固件&#xff09;和常量数据&#xff08;如查找表、字符串等&#xff09;。 特点&#xff1a; 非易失性&#xff1a;断…

【Pytest】setup和teardown的四个级别

文章目录 1.setup和teardown简介2.模块级别的 setup 和 teardown3.函数级别的 setup 和 teardown4.方法级别的 setup 和 teardown5.类级别的 setup 和 teardown 1.setup和teardown简介 在 pytest 中&#xff0c;setup 和 teardown 用于在测试用例执行前后执行一些准备和清理操…

单细胞marker基因表达密度图-(还有一个包装函数)

有小伙伴说想要做单细胞marker基因表达密度图&#xff0c;我一想&#xff0c;好像之前是做过的&#xff08;单细胞marker基因可视化的补充---密度图与等高线图&#xff09;。但是他又说没有文献中的效果。后来我一看&#xff0c;是因为着色的问题。其实用Nebulosa包&#xff08…

记忆化搜索与动态规划:原理、实现与比较

记忆化搜索和动态规划是解决优化问题的两种重要方法&#xff0c;尤其在处理具有重叠子问题和最优子结构性质的问题时非常有效。 目录 1. 记忆化搜索&#xff08;Memoization&#xff09; 定义&#xff1a; 实现步骤&#xff1a; 示例代码&#xff08;斐波那契数列&#xff0…

架构师面试(九):缓存一致性

问题 关于【数据库和缓存】一致性&#xff0c;下面哪几项是在线上生产环境中相对合理的处理方式&#xff1f; A. 对于查询操作&#xff0c;先查缓存&#xff0c;如果为空则查 DB&#xff0c;然后将数据带入缓存&#xff1b; B. 对于插入操作&#xff0c;只写 DB 即可&#…

Spring Boot集成Spring Ai框架【详解 搭建Spring Ai项目,以及简单的ai大模型智能体应用,附有图文+示例代码】

文章目录 一.Spring Ai介绍1.0 认识Spring Ai1.1 特征1.1 大模型专业名字介绍1.1.1 RAG(检索增强生成)RAG 的基本原理RAG 的关键技术RAG 的优势RAG 的应用场景 1.1.2 fine-tuning(微调)1.1.3 function-call(函数调用) 1.2 创建简单的Spring Ai项目 二.Spring Ai简单的智能应用2…

OpenHarmony启动系统-U-Boot简介和源码下载与编译

OpenHarmony系统启动流程简述 设备上电后&#xff0c;OpenHarmony系统大致经历以下3个阶段&#xff1a; 1.BootRom代码引导加载UBoot&#xff1b; 2.UBoot启动初始化硬件资源&#xff0c;引导并加载系统内核(Linux内核)&#xff1b; 3.Kernel(LiteOs,Linux内核)启动、加载驱动…

Metal 学习笔记六:坐标空间

要在网格上轻松找到一个点&#xff0c;您需要一个坐标系。例如&#xff0c;如果网格恰好是您的 iPhone 15 屏幕&#xff0c;则中心点可能是 x&#xff1a;197、y&#xff1a;426。但是&#xff0c;该点可能会有所不同&#xff0c;具体取决于它所处的空间。 在上一章中&#xf…

Java测试框架Mockito快速入门

Mockito结合TestNG快速入门 什么是Mockito Mockito 是一个专门用于 Java 的强大测试框架&#xff0c;主要用来创建和管理模拟对象&#xff0c;辅助开发者进行单元测试&#xff0c;具有以下特点和功能&#xff1a; 创建模拟对象&#xff1a;能通过简洁的语法创建类或接口的模…

LabVIEW 无法播放 AVI 视频的编解码器解决方案

用户在 LabVIEW 中使用示例程序 Read AVI File.vi&#xff08;路径&#xff1a; &#x1f4cc; C:\Program Files (x86)\National Instruments\LabVIEW 2019\examples\Vision\Files\Read AVI File.vi&#xff09;时发现&#xff1a; ✅ LabVIEW 自带的 AVI 视频可正常播放 这是…

AI编程界的集大成者——通义灵码AI程序员

一、引言 随着软件行业的快速发展和技术的进步&#xff0c;人工智能&#xff08;AI&#xff09;正在成为软件开发领域的一个重要组成部分。近年来&#xff0c;越来越多的AI辅助工具被引入到开发流程中&#xff0c;旨在提高效率、减少错误并加速创新。在这样的背景下&#xff0…

第三十三:6.3. 【mitt】 任意组件通讯

概述&#xff1a;与消息订阅与发布&#xff08;pubsub&#xff09;功能类似&#xff0c;可以实现任意组件间通信。 // 引入mitt import mitt from "mitt";// 创建emitter const emitter mitt()/*// 绑定事件emitter.on(abc,(value)>{console.log(abc事件被触发,…

6.7 数据库设计

文章目录 数据库设计6个阶段新奥尔良法完整导图 数据库设计6个阶段 数据库设计是指&#xff0c;根据应用环境&#xff0c;构造数据库模式&#xff0c;建立数据库、应用系统&#xff0c;实现有效地数据存储&#xff0c;以满足用户需求。 数据库设计过程包含6个阶段 数据库规划&…

Java 大视界 -- Java 大数据在智能安防入侵检测与行为分析中的应用(108)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

Vue3实现文件上传、下载及预览全流程详解(含完整接口调用)

文章目录 一、环境准备1.1 创建Vue3项目1.2 安装依赖1.3 配置Element Plus 二、文件上传实现2.1 基础上传组件2.2 自定义上传逻辑&#xff08;Axios实现&#xff09; 三、文件下载实现3.1 直接下载&#xff08;已知文件URL&#xff09;3.2 后端接口下载&#xff08;二进制流&am…

【云原生】SpringCloud-Spring Boot Starter使用测试

目录 Spring Boot Starter是什么&#xff1f; 以前传统的做法 使用 Spring Boot Starter 之后 starter 的理念&#xff1a; starter 的实现&#xff1a; ?创建Spring Boot Starter步骤 在idea新建一个starter项目、直接执行下一步即可生成项目。 ?在xml中加入如下配置…