当前位置: 首页 > news >正文

【从零到一,C++项目实战】CineShare++(基于C++的视频点播系统)

在这里插入图片描述

🌈个人主页: 南桥几晴秋
🌈C++专栏: 南桥谈C++
🌈C语言专栏: C语言学习系列
🌈Linux学习专栏: 南桥谈Linux
🌈数据结构学习专栏: 数据结构杂谈
🌈数据库学习专栏: 南桥谈MySQL
🌈Qt学习专栏: 南桥谈Qt
🌈菜鸡代码练习: 练习随想记录
🌈git学习: 南桥谈Git

🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈
本科在读菜鸡一枚,指出问题及时改正


文章目录

  • 前言
  • 开发环境
  • 第三方库
    • jsoncpp
    • Mysql 数据库 API 认识
    • httplib
  • 项目实现
    • 服务端工具类实现
      • 文件实用工具类
        • 完整代码
        • 功能测试
      • Json实用工具类实现
      • 功能测试
    • 服务端数据管理功能
      • 视频数据表的设计
      • 数据管理类设计
        • 功能测试
    • 服务端业务处理模块
      • 网络通信接口设计
      • 业务处理类模块设计
        • 功能测试
    • 前端界面实现

前言

项目源代码:代码仓库

开发环境

项目名称:CineShare++(基于C++的视频点播系统)

项目功能:搭建一个共享点播系统,服务器能够支持用户通过前端浏览器进行访问,提供清晰的展示、观看和操作界面。该系统不仅可以实现视频的上传功能,还能满足用户观看视频的需求。同时,系统还具备基本的管理功能,包括视频的增、删、改、查等操作,以便管理员对视频内容进行有效管理和维护。通过此系统,用户可以便捷地上传和浏览视频,管理员则能确保内容的有序管理与更新。

开发环境包括:Ubuntu 22.04 操作系统VSCode 编辑器g++ 编译器gdb 调试工具,以及 Makefile 构建工具

技术特点包括:HTTP 服务器搭建、RESTful 风格接口设计、JSON 序列化处理、线程池管理,以及 HTML、CSS 和 JavaScript 基础开发。这些技术共同支持系统的高效性、可扩展性和用户体验。

项目模块:

  • 数据管理模块:基于Mysql进行数据管理
  • 业务处理模块:基于httplib搭建http,使用RESTful 风格接口设计处理客户端需求
  • 前端界面模块:基于基础的HTML+CSS+JS完成基于简单模板前端界⾯的修改与功能完成

第三方库

jsoncpp

json 是⼀种数据交换格式,采⽤完全独⽴于编程语⾔的⽂本格式来存储和表⽰数据。

这个第三方库在文章【Linux网络编程】应用层:自定义协议 | 序列化和反序列化 | 系统角度理解read、write、recv、 send 和 tcp 为什么支持全双工 | 实现网络版计算器 | jsoncpp库中介绍过以及了解如何安装jsoncpp库。

  • json 数据类型:对象,数组,字符串,数字
  • 对象:使⽤花括号 {} 括起来的表⽰⼀个对象。
  • 数组:使⽤中括号 [] 括起来的表⽰⼀个数组。
  • 字符串:使⽤常规双引号 "" 括起来的表⽰⼀个字符串

在本项目直接使用jsoncpp的接口,不需要我们自己去写字符串序列化与反序列化,这样减少了项目的开发周期。

jsoncpp实现的功能:

  • 实现json格式的序列化与反序列化
  • 完成将多个数据对象组织称为json格式字符串
  • 完成将json格式字符串解析得到多个数据对象

这三个功能主要借助三个类以及对应的成员函数来实现的:

  • Json::Value:主要实现中间数据的存储
class Json::Value{Value &operator=(const Value &other); //Value重载了[]和=,因此所有的赋值和获取数据都可以通过Value& operator[](const std::string& key);//简单的⽅式完成 val["姓名"] = "⼩明";Value& operator[](const char* key);Value removeMember(const char* key);//移除元素const Value& operator[](ArrayIndex index) const; //val["成绩"][0]Value& append(const Value& value);//添加数组元素val["成绩"].append(88);ArrayIndex size() const;//获取数组元素个数 val["成绩"].size();std::string asString() const;//转string string name =val["name"].asString();const char* asCString() const;//转char* char *name =val["name"].asCString();Int asInt() const;//转int int age = val["age"].asInt();float asFloat() const;//转floatbool asBool() const;//转 bool
};
  • JSON_API StreamWriter类:用于对Json::Value中数据进行序列化
class JSON_API StreamWriter {virtual int write(Value const& root, std::ostream* sout) = 0;
}
  • JSON_API CharReader:实现反序列化,解析得到的对象存储到Json::Value
class JSON_API CharReader {virtual bool parse(char const* beginDoc, char const* endDoc,Value* root, std::string* errs) = 0;
}

将Value中存储的数据转换成Json字符串,实现序列化:

#include<iostream>
#include<sstream>
#include<string>
#include<memory>
#include<jsoncpp/json/json.h>int main()
{const char *name = "Gavin";int age = 21;float score[] = {150, 149, 148};Json::Value val;val["Name"] = name;val["Age"] = age;val["Score"].append(score[0]);val["Score"].append(score[1]);val["Score"].append(score[2]);Json::StreamWriterBuilder swb;// 设置 JSON 输出不转义 Unicode 字符//swb.settings_["indentation"] = "";  // 去掉缩进swb.settings_["ensureAscii"] = false;  // 使中文字符不被转义std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss;int ret = sw->write(val,&ss);if(ret != 0){std::cout<<"write failed!\n";return -1;}std::cout << ss.str() <<std::endl;return 0;
}

在这里插入图片描述

将Json字符串转换到原始格式,实现反序列化:

void unserialize(const std::string &str)
{Json::Value val;Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err;bool ret = cr->parse(str.c_str(),str.c_str()+str.size(),&val,&err);if(ret==false){std::cout << "parse failed\n";return ;}std::cout << val["Name"] <<std::endl;std::cout << val["Age"] <<std::endl;int sz = val["Score"].size();for(int i=0;i<sz;i++)std::cout << val["Score"][i] <<std::endl;
}int main()
{//serialize();std::string str = R"({"Name":"Gavin", "Age":"22", "Score":[66, 77, 88]})";unserialize(str);return 0;
}

在这里插入图片描述

Mysql 数据库 API 认识

MySQL操作句柄初始化

MYSQL *mysql_init(MYSQL *mysql)

连接MySQL服务器

MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user,const char *passwd,const char *db, unsigned int port,
const char *unix_socket, unsigned long client_flag);//mysql--初始化完成的句柄
//host---连接的mysql服务器的地址
//user---连接的服务器的⽤⼾名
//passwd-连接的服务器的密码
//db ----默认选择的数据库名称
//port---连接的服务器的端⼝: 默认0是3306端⼝
//unix_socket---通信管道⽂件或者socket⽂件,通常置NULL
//client_flag---客⼾端标志位,通常置0
//返回值:成功返回句柄,失败返回NULL
//设置当前客⼾端的字符集

设置当前客户端的字符集

int mysql_set_character_set(MYSQL *mysql, const char *csname)//mysql--初始化完成的句柄
//csname--字符集名称,通常:"utf8"
//返回值:成功返回0, 失败返回⾮0;
//选择操作的数据库

选择操作的数据库

int mysql_select_db(MYSQL *mysql, const char *db)

执行sql语句:

int mysql_query(MYSQL *mysql, const char *stmt_str)
//mysql--初始化完成的句柄
//stmt_str--要执⾏的sql语句

保存查询结果到本地

MYSQL_RES *mysql_store_result(MYSQL *mysql)

获取结果集中的⾏数与列数

uint64_t mysql_num_rows(MYSQL_RES *result)//result--保存到本地的结果集地址
//返回值:结果集中数据的条数unsigned int mysql_num_fields(MYSQL_RES *result)
//result--保存到本地的结果集地址
//返回值:结果集中每⼀条数据的列数

遍历结果

MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)
//result--保存到本地的结果集地址
//返回值:实际上是⼀个char **的指针,将每⼀条数据做成了字符串指针数组 row[0]-第0列row[1]-第1列
//并且这个接⼝会保存当前读取结果位置,每次获取的都是下⼀条数据

释放结果集

oid mysql_free_result(MYSQL_RES *result)
//result--保存到本地的结果集地址
//返回值:void

关闭数据库客⼾端连接,销毁句柄

void mysql_close(MYSQL *mysql)

获取mysql接口执⾏错误原因

const char *mysql_error(MYSQL *mysql)

测试样例:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>void add(MYSQL *mysql)
{char *sql = "insert into test_tb values(NULL, 22, '小高', 88);";int ret = mysql_query(mysql, sql);if (ret != 0){printf("mysql query failed, err: %d, error: %s\n", mysql_errno(mysql), mysql_error(mysql));return;}
}void mod(MYSQL *mysql)
{char *sql = "update test_tb set age=25 where name='小高';";int ret = mysql_query(mysql,sql);if (ret != 0){printf("mysql query failed, err: %d, error: %s\n", mysql_errno(mysql), mysql_error(mysql));return;}
}void del(MYSQL *mysql)
{char *sql = "delete from test_tb where name='小高';";int ret = mysql_query(mysql,sql);if (ret != 0){printf("mysql query failed, err: %d, error: %s\n", mysql_errno(mysql), mysql_error(mysql));return;}
}void get(MYSQL *mysql)
{char *sql = "select * from test_tb;";int ret = mysql_query(mysql, sql);if (ret != 0){printf("mysql query failed, err: %d, error: %s\n", mysql_errno(mysql), mysql_error(mysql));return;}MYSQL_RES *res = mysql_store_result(mysql);if (res == NULL){printf("mysql store result error:%s\n", mysql_error(mysql));return;}int row = mysql_num_rows(res);int col = mysql_num_fields(res);printf("%10s%10s%10s%10s\n", "ID", "年龄", "姓名", "成绩");for (int i = 0; i < row; i++){MYSQL_ROW row_data = mysql_fetch_row(res);for (int i = 0; i < col; i++){printf("%10s", row_data[i]);}printf("\n");}mysql_free_result(res);return;
}int main()
{MYSQL *mysql = mysql_init(NULL);if ((mysql_real_connect(mysql, "127.0.0.1", "root", "123456", "test_db", 0, NULL, 0)) == NULL){printf("mysql connect failed: %d, error: %s\n", mysql_errno(mysql), mysql_error(mysql));return -1;}mysql_set_character_set(mysql, "utf8");//add(mysql);//add(mysql);//mod(mysql);//del(mysql);get(mysql);mysql_close(mysql);  // 关闭连接return 0;
}

httplib

httplib 库,⼀个 C++11 单⽂件头的跨平台HTTP/HTTPS库。

httplib 库实际上是⽤于搭建⼀个简单的 http 服务器或者客户端的库,这种第三⽅⽹络库,可以让我们免去搭建服务器或客⼾端的时间,把更多的精⼒投⼊到具体的业务处理中,提⾼开发效率。

在这里插入图片描述

测试用例

#include"./httplib.h"
using namespace httplib;void HelloGavin(const Request &req, Response &rsp)
{rsp.body = "Hello Gavin!";rsp.status = 200;
}
void Numbers(const Request &req, Response &rsp)
{std::string num = req.matches[1]; // matches存放正则表达式匹配的规则数据,也就是()里面单独捕捉的数据rsp.set_content(num, "Text/plain");rsp.status = 200; // 向rsp.body里面添加数据
}
void Multipart(const Request &req, Response &rsp)
{if(req.has_file("file1") == false){rsp.status = 100;return;}const auto& file = req.get_file_value("file1");    std::cout << file.filename << std::endl;std::cout << file.content << std::endl;rsp.status = 200;
}
int main()
{Server server;// 设置一个静态资源目录server.set_mount_point("/","./www");// 添加请求:处理函数映射信息server.Get("/hi",HelloGavin);// 正则表达式:\d表示数字,+表示匹配匹配前面的字符一次或者多次,()表示单独捕捉数据server.Get("/numbers/(\\d+)",Numbers);server.Post("/multipart",Multipart);server.listen("0.0.0.0", 8888);return 0;
}

在这里插入图片描述

项目实现

服务端工具类实现

文件实用工具类

在视频点播系统中因为涉及到⽂件上传,需要对上传的⽂件进⾏备份存储,因此⾸先设计封装⽂件操作类,这个类封装完毕之后,则在任意模块中对⽂件进⾏操作时都将变的简单化。

功能:

  1. 获取文件大小(属性)
  2. 判断文件是否存在
  3. 向文件写入数据
  4. 从文件读取数据
  5. 针对目录文件多创建一个目录

工具类的框架

namespace aod
{class FileUtil {public:FileUtil(const std::string name):_name(name){}bool Exists(); // 判断文件是否存在size_t Size(); // 获取文件大小bool ReadContent(std::string *body); // 读取文件数据到body中bool WriteContent(const std::string &body); //向文件写入数据bool CreateDirectory(); // 针对目录创建目录private:std::string _name;};
}

判断文件是否存在

调用Linux中access()函数:

#include <unistd.h>
int access(const char *pathname, int mode);

成功返回0,否则出错返回-1

FileUtil(const std::string name):_name(name){}
// 判断文件是否存在
bool Exists()
{int ret = access(_name.c_str(), F_OK); // F_OK专门用于检测文件是否存在:存在返回0,不存在返回-1if(ret != 0) return false;return true;
}

获取文件大小

Linux中获取文件属性可以调用stat()函数

 #include <sys/types.h>#include <sys/stat.h>#include <unistd.h>int stat(const char *pathname, struct stat *statbuf);

功能实现:

// 获取文件大小
size_t Size()
{if(this->Exists() == false) return 0;struct stat st;int ret = stat(_name.c_str(), &st);if(ret != 0) return 0;return st.st_size;
}

读取文件数据到body中

  • 实例化一个文件输入流
  • 以二进制形式打开文件
  • 获取文件大小
  • 重置缓冲区大小
  • 将文件依次输入到缓冲区中
  • 成功后关闭文件
        // 读取文件数据到body中bool ReadContent(std::string *body){std::ifstream ifs;ifs.open(_name, std::ios::binary); // 以二进制方式打开if(ifs.is_open() == false){std::cout << "open failed!\n";return false;}size_t flen = this->Size();body->resize(flen);ifs.read(&(*body)[0], flen);//body[0] 是 std::string 对象的第一个字符,而不是字符串的底层缓冲区地址// (*body) 获取指针 body 指向的 std::string 对象,//然后 (*body)[0] 获取该字符串的第一个字符,&(*body)[0] 获取该字符的地址,也就是底层数据的起始位置if(ifs.good() == false){std::cout << "read file content failed!\n";ifs.close();return false;}ifs.close();return true;}

向文件写入数据

  • 实例化一个文件输出流
  • 以二进制形式打开
  • 写入数据
  • 写入成功后关闭文件
 //向文件写入数据bool WriteContent(const std::string &body){std::ofstream ofs;ofs.open(_name, std::ios::binary); // 以二进制方式打开if(ofs.is_open() == false){std::cout << "open failed!\n";return false;}ofs.write(body.c_str(), body.size());if(ofs.good() == false){std::cout << "write file content failed!\n";ofs.close();return false;}ofs.close();return true;}

针对目录创建目录

在Linux中调用mkdir()函数:

#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);

功能实现:

 // 针对目录创建目录bool CreateDirectory(){if(this->Exists()) return true;mkdir(_name.c_str(),0777);return true;}
完整代码
#ifndef __MY_UTIL__
#define __MY_UTIL__
#include<iostream>
#include<fstream>
#include<string>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>namespace aod
{class FileUtil {public:FileUtil(const std::string name):_name(name){}// 判断文件是否存在bool Exists(){int ret = access(_name.c_str(), F_OK); // F_OK专门用于检测文件是否存在:存在返回0,不存在返回-1if(ret != 0){std::cout << "file is not exists!\n";return false;}return true;}// 获取文件大小size_t Size(){if(this->Exists() == false) return 0;struct stat st;int ret = stat(_name.c_str(), &st);if(ret != 0) {std::cout << "get file stat failed!\n";   return 0;}return st.st_size;}// 读取文件数据到body中bool ReadContent(std::string *body){std::ifstream ifs;ifs.open(_name, std::ios::binary); // 以二进制方式打开if(ifs.is_open() == false){std::cout << "open failed!\n";return false;}size_t flen = this->Size();body->resize(flen);ifs.read(&(*body)[0], flen);//body[0] 是 std::string 对象的第一个字符,而不是字符串的底层缓冲区地址// (*body) 获取指针 body 指向的 std::string 对象,//然后 (*body)[0] 获取该字符串的第一个字符,&(*body)[0] 获取该字符的地址,也就是底层数据的起始位置if(ifs.good() == false){std::cout << "read file content failed!\n";ifs.close();return false;}ifs.close();return true;}//向文件写入数据bool WriteContent(const std::string &body){std::ofstream ofs;ofs.open(_name, std::ios::binary); // 以二进制方式打开if(ofs.is_open() == false){std::cout << "open failed!\n";return false;}ofs.write(body.c_str(), body.size());if(ofs.good() == false){std::cout << "write file content failed!\n";ofs.close();return false;}ofs.close();return true;}// 针对目录创建目录bool CreateDirectory(){if(this->Exists()) return true;mkdir(_name.c_str(),0777);return true;}private:std::string _name;};
}#endif
功能测试
void FileTest1()
{aod::FileUtil("./www").CreateDirectory();aod::FileUtil("./www/index.html").WriteContent("<html></html>");std::string body;aod::FileUtil("./www/index.html").ReadContent(&body);std::cout << body << std::endl;std::cout << aod::FileUtil("./www/index.html").Size() <<std::endl;
}

在这里插入图片描述

Json实用工具类实现

主要实现功能:

  1. 实现序列化
  2. 实现反序列化

工具框架

// Json工具类
class JsonUtil
{
public:static bool Serialize(const Json::Value &value ,std::string *body);static bool Unserialize(const std::string &body, Json::Value *value);
};

序列化

  • Json::StreamWriterBuilder:这是 JsonCpp 提供的一个构建器,用于配置并生成一个流写入器 StreamWriter。该写入器负责将 Json::Value 转换为 JSON 格式的字符串。
  • std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter()):创建一个流写入器对象 sw,它会根据 StreamWriterBuilder 中的配置来序列化 JSON 数据。
  • std::stringstream ss:一个字符串流,用于暂时存储序列化后的结果。
  • sw->write(value, &ss):执行序列化操作,将 value 序列化到字符串流 ss 中。write 函数返回值为 0 时表示成功,非 0 则表示失败。
  • *body = ss.str():将字符串流 ss 中的内容转换为字符串,并赋值给 body
static bool Serialize(const Json::Value &value ,std::string *body)
{Json::StreamWriterBuilder swb;std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss;int ret = sw->write(value, &ss);if(ret != 0){std::cout << "Serialize failed!\n";return;}*body = ss.str();return true;
}static bool Unserialize(const std::string &body, Json::Value *value);
};

反序列化

  • 定义一个智能指针对象
  • 将智能指针指向的对象直接反序列化
  • 反序列化成功直接将结果传给了vlaue
static bool Unserialize(const std::string &body, Json::Value *value)
{Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err;bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), value, &err);if(ret == false){std::cout << "Unserialize failed!\n";return false;}return true;
}

功能测试

void JsonTest1()
{Json::Value val;val["Name"] = "Gavin";val["Sex"] = "man";val["Age"] = 21;val["Score"].append(110.5);   val["Score"].append(110.5);   val["Score"].append(120.5);std::string body;aod::JsonUtil::Serialize(val, &body);std::cout << body <<std::endl;Json::Value stu;aod::JsonUtil::Unserialize(body, &stu);std::cout << stu["Name"].asString() <<std::endl;   std::cout << stu["Sex"].asString() <<std::endl;   std::cout << stu["Age"].asString() <<std::endl;for(auto &a : stu["Score"]){std::cout << a.asFloat() <<std::endl;}   
}

在这里插入图片描述

服务端数据管理功能

视频数据表的设计

使用 MySQL数据库管理视频信息,只管理视频及图片的信息,视频以及图片是存储的文件中。

本项目中只是完成一个简单的视频信息表:

  • 视频ID
  • 视频名称
  • 视频描述信息
  • 视频文件的url路径(只是链接)
  • 视频封面图片的url路径(只是链接)
create database if not exists aod_system;
use aod_system;
create table if not exists tb_video(
id int primary key auto_increment comment '视频ID',
name varchar(32) comment '视频名称',
info text comment '视频描述',
video varchar(256) comment '视频⽂件url,加上静态资源根⽬录就是实际存储路径',
image varchar(256) comment '封⾯图⽚⽂件url,加上静态资源根⽬录就是实际存储路径'
);

在这里插入图片描述

数据管理类设计

本模块主要为了实现对数据库的增删查改管理,但是实际的项目中,会存在多个表,每个表要操作的数据不一样,因此对数据进行增删查改时需要为每一个表进行创建一个类。

视频操作:

  • 新增
  • 修改
  • 删除
  • 查询所有
  • 查询单个
  • 模糊匹配

视频信息在接口直接的传递因为字段数量可能很多,因此需要用Json::Value对象进行存储

基本框架

static MYSQL *MysqlInit();
static void MysqlDestory();
static bool MysqlQuery(MYSQL *mysql, const std::string &sql);
class TableVideo
{
public:TableVideo();~TableVideo();bool Insert(const Json::Value &video);bool Update(int video_id, const Json::Value &video);bool Delete(int video_id);bool SelectAll(Json::Value *videos);bool SelectOne(int video_id, const Json::Value *video);bool SelectLike(const std::string &key, Json::Value *videos);
private:MYSQL *mysql;std::mutex _mutex;
};

初始化

  • 初始化一个句柄
  • 建立连接
  • 设置字符集
  • 将初始化的句柄返回给外部
static MYSQL *MysqlInit()
{MYSQL *mysql = mysql_init(NULL);if(mysql == NULL){std::cout << "Init failed!\n";return NULL;}if(mysql_real_connect(mysql, HOST, USER, PASS, NAME, 0, NULL, 0) ==NULL){std::cout << "connect failed!\n";mysql_close(mysql);return NULL;}mysql_set_character_set(mysql,"utf8");return mysql;
}

销毁

  • 句柄不为空时直接销毁即可
static void MysqlDestory(MYSQL *mysql)
{if(mysql != NULL){mysql_close(mysql);}return;
}

语句执行

static bool MysqlQuery(MYSQL *mysql, const std::string &sql)
{int ret = mysql_query(mysql, sql.c_str());if(ret != 0){std::cout << sql << std::endl;std::cout << mysql_error(mysql) << std::endl;return false; }return true;
}

构造与析构

  • 直接调用初始化接口以及销毁接口
TableVideo()
{_mysql = MysqlInit();if(_mysql == NULL) exit(-1);
}~TableVideo()
{MysqlDestory(_mysql);
}

新增操作

  • 定义一个sql表示查询语句
  • 调整sql的大小
  • 设置一个sql语句模版
  • 格式化字符串填充语句
bool Insert(const Json::Value &video)
{std::string sql;sql.resize(4096 + video["info"].asString().size()); // 防止简介过长#define INSERT_VIDEO "INSERT tb_video values(null, '%s', '%s', '%s', '%s');"sprintf(&sql[0], INSERT_VIDEO, video["name"].asString(),video["info"].asString(),video["video"].asString(),video["image"].asString());return MysqlQuery(_mysql,sql);
}

修改操作

bool Update(int video_id, const Json::Value &video)
{std::string sql;sql.resize(4096 + video["info"].asString().size()); // 防止简介过长#define UPDATE_VIDEO "update tb_video set name='%s', info='%s' where id=%d;"sprintf(&sql[0], UPDATE_VIDEO, video["name"].asString(),video["info"].asString(),video_id);return MysqlQuery(_mysql,sql);
}

删除操作

bool Delete(int video_id)
{#define DELETE_VIDEO "delete from tb_video where id=%d;"char sql[1024] = {0};sprintf(sql, DELETE_VIDEO, video_id);return MysqlQuery(_mysql, sql);
}

查询所有数据

  • 类实例化的对象可能在多线程环境下,因此需要保证在查询结果后将数据保存到本地操作时原子的,避免一个线程还没有保存到本地,另一个线程开始查询,此时线程就会
  • 处理查新结果:将查询到的结果存储在MYSQL_RES结构体中
  • 遍历查询结果:
    • mysql_num_rows(res):获取查询结果的行数,即视频记录的数量
    • mysql_fetch_row(res):遍历查询结果集,返回当前行的数据(每行是一个MYSQL_ROW 数组)
    • 在遍历过程中,使用atoi 函数将 row[i] 转换为整数并赋值给 video 对象的相应字段
bool SelectAll(Json::Value *videos)
{#define SELECTALL_VIDEO "select * from tb_video;"_mutex.lock();bool ret = MysqlQuery(_mysql, SELECTALL_VIDEO);if(ret == false){_mutex.unlock();return false;}MYSQL_RES *res = mysql_store_result(_mysql);if(res == NULL){std::cout << "mysql store result failed!\n";_mutex.unlock();return false;}_mutex.unlock();int num_rows = mysql_num_rows(res);for(int i = 0; i < num_rows; i++){MYSQL_ROW row = mysql_fetch_row(res);Json::Value video;video["id"] = atoi(row[0]);video["name"] = atoi(row[1]);video["info"] = atoi(row[2]);video["video"] = atoi(row[3]);video["image"] = atoi(row[4]);videos->append(video);}mysql_free_result(res);return true;
}

查询单个数据

bool SelectOne(int video_id, Json::Value *video)
{#define SELECTONE_VIDEO "select * from tb_video where id=%d;"char sql[1024] = {0};sprintf(sql, SELECTONE_VIDEO, video_id);_mutex.lock();bool ret = MysqlQuery(_mysql, sql);if(ret == false){_mutex.unlock();return false;}MYSQL_RES *res = mysql_store_result(_mysql);if(res == NULL){std::cout << "mysql store result failed!\n";_mutex.unlock();return false;}_mutex.unlock();int num_rows = mysql_num_rows(res);if(num_rows != 1){std::cout << "hace no data!\n";mysql_free_result(res);return false;}MYSQL_ROW row = mysql_fetch_row(res);(*video)["id"] = video_id;(*video)["name"] = row[1];(*video)["info"] = row[2];(*video)["video"] = row[3];(*video)["image"] = row[4];mysql_free_result(res);return true;            
}

模糊匹配

bool SelectLike(const std::string &key, Json::Value *videos)
{#define SELECTLIKE_VIDEO "select * from tb_video where name like '%%%s%%';"char sql[2014] = {0};sprintf(sql, SELECTLIKE_VIDEO, key.c_str());_mutex.lock();bool ret = MysqlQuery(_mysql, sql);if(ret == false){_mutex.unlock();return false;}MYSQL_RES *res = mysql_store_result(_mysql);if(res == NULL){std::cout << "mysql store result failed!\n";_mutex.unlock();return false;}_mutex.unlock();int num_rows = mysql_num_rows(res);for(int i = 0; i < num_rows; i++){MYSQL_ROW row = mysql_fetch_row(res);Json::Value video;video["id"] = atoi(row[0]);video["name"] = atoi(row[1]);video["info"] = atoi(row[2]);video["video"] = atoi(row[3]);video["image"] = atoi(row[4]);videos->append(video);}mysql_free_result(res);return true;        
}
功能测试
void DataTest()
{aod::TableVideo tb_video;Json::Value video;// video["name"] = "Jhon";// video["info"] = "a man but not a student";// video["video"] = "/video/man.ma4";// video["image"] = "/image/man.jpg";//tb_video.Insert(video);// tb_video.Update(2, video);// tb_video.SelectAll(&video);// tb_video.SelectOne(1, &video);// tb_video.SelectLike("Gavin", &video);// std::string body;// aod::JsonUtil::Serialize(video, &body);// std::cout << body <<std::endl;tb_video.Delete(2);
}

服务端业务处理模块

网络通信接口设计

http协议其实是一种数据格式,是一个tcp传输,在应用层采用的一种数据特定格式

网络通信接口设计就是定义好什么样的请求是一个查询请求,什么的请求是一个删除请求。

服务端提供的功能:新增视频、删除视频、修改视频、查询所有视频信息、查询单个视频、模糊匹配视频

接口设计:使用restful接口设计
RESTful 架构可以充分的利⽤HTTP协议的各种功能,是 HTTP 协议的最佳实践,正⽂通常采⽤ JSON 格式,使用GET表示查询,POST表示新增,PUT表示修改,DELETE表示删除,并且正文资源数据采用jsonhtml格式。

业务处理类模块设计

本项目中,业务处理主要包含两个功能:

  1. 网络通信功能的实现
  2. 业务功能处理的实现

业务处理模块负责与客⼾端进⾏⽹络通信,接收客⼾端的请求,然后根据请求信息,明确客户端端用户的意图,进⾏业务处理,并进⾏对应的结果响应。

网络通信借助httplib库即可快速搭建,因此完成此项目重点就可以在业务处理中。

业务处理功能包括:

  1. 客户端的视频数据和信息上传
  2. 客户端视频列表显示(查询所有视频信息)
  3. 客户端数据观看请求(视频数据的获取)
  4. 客户端对视频的其他管理(删除、修改)

业务处理模块框架

#include"data.hpp"
#include"httplib.h"namespace aod
{
#define WWWROOT "./www"
#define VIDEO_ROOT "./video/"   // 实际上是"./www/video/"
#define VIDEO_IMAGE "./image"TableVideo *tb_video = NULL;class Server{public:Server(int port): _port(port){}bool RunModule();private:int _port;httplib::Server _ser;private:static void Insert(const httplib::Request &req, httplib::Response &res); // 这里需要定义成一个静态成员函数// 静态成员函数的参数不包括this指针,确保这个函数只有两个参数static void Update(const httplib::Request &req, httplib::Response &res);static void Delete(const httplib::Request &req, httplib::Response &res);static void GetOne(const httplib::Request &req, httplib::Response &res);static void GetAll(const httplib::Request &req, httplib::Response &res);};
}

启动服务器

  • 首先初始化数据管理模块,创建指定的地址
  • 搭建http服务器,使用httplib
bool RunModule()
{//1.初始化数据管理模块,创建指定的目录tb_video = new TableVideo();FileUtil(WWW_ROOT).CreateDirectory();std::string video_real_path = WWW_ROOT + VIDEO_ROOT;FileUtil(video_real_path).CreateDirectory();std::string image_real_path = WWW_ROOT + IMAGE_ROOT;//2.搭建http服务器//a.设置静态资源根目录_srv.set_mount_point("/", WWW_ROOT);//b.添加请求与处理函数的映射管理_srv.Post("/video", Insert);_srv.Delete("/video/(\\d+)", Delete);_srv.Put("/video/(\\d+)", Update);_srv.Get("/video/(\\d+)", GetOne);_srv.Get("/video", GetAll);//c.启动_srv.listen("0.0.0.0", _port);return true;
}

新增数据

  • 从请求中获取文件字段
  • 将文件内容写到指定的路径
  • 创建一个Json对象存储数据到数据库中
static void Insert(const httplib::Request &req, httplib::Response &rsp)
{if(req.has_file("name") == false || req.has_file("info") == false ||req.has_file("video") == false ||req.has_file("image") == false){rsp.status = 400;rsp.body = R"({"result":false, "reason":"上传的数据信息错误"})";rsp.set_header("Content-type", "application/json");                    }auto name = req.get_file_value("name");auto info = req.get_file_value("info");auto video = req.get_file_value("video");auto image = req.get_file_value("image");std::string video_name = name.content;std::string video_info = info.content;std::string video_path = WWW_ROOT + VIDEO_ROOT + name.filename + video.filename;std::string image_path = WWW_ROOT + IMAGE_ROOT + name.filename + image.filename;if((FileUtil(video_path).WriteContent(video.content)) == NULL){rsp.status = 500;rsp.body = R"({"result":false, "reason":"视频文件存储失败"})";rsp.set_header("Content-type", "application/json");}if((FileUtil(image_path).WriteContent(image.content)) == NULL){rsp.status = 500;rsp.body = R"({"result":false, "reason":"图片文件存储失败"})";rsp.set_header("Content-type", "application/json");                }Json::Value video_json;video_json["name"] = video_name;video_json["info"] = video_info;video_json["video"] = VIDEO_ROOT + name.filename + video.filename;video_json["image"] = IMAGE_ROOT + name.filename + image.filename;if((tb_video->Insert(video_json)) == NULL){rsp.status = 500;rsp.body = R"({"result":false, "reason":"新增数据失败"})";rsp.set_header("Content-type", "application/json");                }return ;
}

修改操作

功能测试
void ServerTest()
{aod::Server server(8888);server.RunModule();
}

新增

在这里插入图片描述

在这里插入图片描述

查询

在这里插入图片描述

模糊匹配

在这里插入图片描述

修改

在这里插入图片描述

在这里插入图片描述

删除视频信息

在这里插入图片描述

在这里插入图片描述

前端界面实现

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content=""><meta name="author" content="OrcasThemes"><meta http-equiv="X-UA-Compatible" content="IE=Edge" /><title>Home</title><!-- Bootstrap core CSS --><link href="css/bootstrap.css" rel="stylesheet"><!-- Custom styles for this template --><link rel="stylesheet" href="css/screen.css"><link rel="stylesheet" href="css/animation.css"><!--[if IE 7]><![endif]--><link rel="stylesheet" href="css/font-awesome.css"><!--[if lt IE 8]><link rel="stylesheet" href="css/ie.css" type="text/css" media="screen, projection"><![endif]--><link href="css/lity.css" rel="stylesheet"><style>[v-cloak] {display: none;}</style>
</head>
<body>
<div id="myapp">
<!-- HOME 1 -->
<div id="home1" class="container-fluid standard-bg"><!-- HEADER --><div class="row header-top"><div class="col-lg-3 col-md-6 col-sm-5 col-xs-8"><a class="gwj_logo" href="#"><img src="img/gwj_logo.png" class="main-logo img-responsive" alt="Muvee Reviews" title="Muvee Reviews"></a></div><div class="col-lg-6 hidden-md text-center hidden-sm hidden-xs"></div><div class="col-lg-3 col-md-6 col-sm-7 hidden-xs"><div class="right-box"><button type="button" class="access-btn" data-toggle="modal" data-target="#enquirypopup">新增视频</button></div></div></div><!-- MENU --><div class="row home-mega-menu "><div class="col-md-12"><nav class="navbar navbar-default"><div class="navbar-header"><button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".js-navbar-collapse"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button></div><div class="collapse navbar-collapse js-navbar-collapse megabg dropshd "><ul class="nav navbar-nav"><li><a href="index.html">视频点播</a></li></ul><div class="search-block"><form><input type="search" placeholder="Search"></form></div></div><!-- /.nav-collapse --></nav></div></div><!-- CORE --><div class="row"><!-- SIDEBAR --><div class="col-lg-2 col-md-4 hidden-sm hidden-xs"></div><!-- HOME MAIN POSTS -->	<div class="col-lg-10 col-md-8"><section id="home-main"><h2 class="icon"><i class="fa fa-television" aria-hidden="true"></i>视频列表</h2><div class="row"><!-- ARTICLES --><div class="col-lg-9 col-md-12 col-sm-12"><div class="row auto-clear"><article class="col-lg-3 col-md-6 col-sm-4" v-for="video in videos"><!-- POST L size --><div class="post post-medium"><div class="thumbr"><a class="afterglow post-thumb" v-bind:href="'/video.html?id='+video.id" target="_blank"><span class="play-btn-border" title="Play"><i class="fa fa-play-circle headline-round" aria-hidden="true"></i></span><div class="cactus-note ct-time font-size-1"><span>02:02</span></div><img class="img-responsive" v-bind:src="video.image" alt="#" v-cloak></a></div><div class="infor"><h4><a class="title" href="#" v-cloak>{{video.name}}</a></h4><span class="posts-txt" title="Posts from Channel"><i class="fa fa-thumbs-up" aria-hidden="true"></i>20.895</span><div class="ratings"><i class="fa fa-star" aria-hidden="true"></i><i class="fa fa-star" aria-hidden="true"></i><i class="fa fa-star-half-o" aria-hidden="true"></i><i class="fa fa-star-o"></i><i class="fa fa-star-half"></i></div></div></div></article></div><div class="clearfix spacer"></div></div><!-- RIGHT ASIDE --><div class="col-lg-3 hidden-md col-sm-12 text-center top-sidebar"></div></div></section></div></div>
</div>
<!-- CHANNELS -->
<div id="channels-block" class="container-fluid channels-bg">
</div>
<!-- BOTTOM BANNER -->
</div>
<!-- FOOTER -->
<div id="footer" class="container-fluid footer-background"><div class="container"><footer><!-- SECTION FOOTER --><div class="row"><!-- SOCIAL --><div class="col-lg-3 col-md-3 col-sm-6 col-xs-12"><div class="row auto-clear"></div></div><!-- TAGS --><div class="col-lg-3 col-md-3 col-sm-6 col-xs-12"></div><!-- POST --><div class="col-lg-3 col-md-3 col-sm-6 col-xs-12"></div><!-- LINKS --><div class="col-lg-3 col-md-3 col-sm-6 col-xs-12"></div></div><div class="row copyright-bottom text-center"><div class="col-md-12 text-center"><a href="" class="footer-logo" title="Video Magazine Bootstrap HTML5 template"><img src="img/gwj_logo.png" class="img-responsive text-center" alt="Video Magazine Bootstrap HTML5 template"></a>	<p v-cloak>HAUE &copy; Author by Gaowenjun</p></div></div></footer></div>
</div>
<!-- MODAL -->
<div id="enquirypopup" class="modal fade in " role="dialog"><div class="modal-dialog"><!-- Modal content--><div class="modal-content row"><div class="modal-header custom-modal-header"><button type="button" class="close" data-dismiss="modal">×</button><h2 class="icon"><i class="fa fa-television" aria-hidden="true"></i>新增视频</h2></div><div class="modal-body"><form name="info_form" class="form-inline" action="/add" method="post" enctype="multipart/form-data"><div class="form-group col-sm-12"><input type="text" class="form-control" name="name" placeholder="输入视频名称"></div><div class="form-group col-sm-12"><input type="text" class="form-control" name="info" placeholder="输入视频简介"></div><div class="form-group col-sm-12"><input type="file" class="form-control" name="video" placeholder="选择视频文件"></div><div class="form-group col-sm-12"><input type="file" class="form-control" name="image" placeholder="选择封面图片"></div><div class="form-group col-sm-12"><button class="subscribe-btn pull-right" type="submit" title="Subscribe">上传</button></div></form></div></div></div>
</div>
</div>
</body><!-- JAVA SCRIPT -->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="js/jquery-1.12.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/lity.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>$(".nav .dropdown").hover(function() {$(this).find(".dropdown-toggle").dropdown("toggle");});
</script>
<script>let app = new Vue({el: '#myapp',data: {author: "Gaowenjun",videos:[]},methods : {get_allvideos : function() {$.ajax({url: "/video",type: "get",context: this,success : function(result, status, xhr){this.videos = result;}})}}});app.get_allvideos();
</script>
</html>

video.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content=""><meta name="author" content="OrcasThemes"><meta http-equiv="X-UA-Compatible" content="IE=Edge" /><title>Home</title><!-- Bootstrap core CSS --><link href="css/bootstrap.css" rel="stylesheet"><!-- Custom styles for this template --><link rel="stylesheet" href="css/screen.css"><link rel="stylesheet" href="css/animation.css"><!--[if IE 7]><![endif]--><link rel="stylesheet" href="css/font-awesome.css"><!--[if lt IE 8]><link rel="stylesheet" href="css/ie.css" type="text/css" media="screen, projection"><![endif]--><link href="css/lity.css" rel="stylesheet"><style>[v-cloak] {display: none;}</style>
</head>
<body>
<div id="myapp">
<!-- HOME 1 -->
<div id="home1" class="container-fluid standard-bg"><!-- HEADER --><div class="row header-top"><div class="col-lg-3 col-md-6 col-sm-5 col-xs-8"><a class="gwj_logo" href="#"><img src="img/gwj_logo.png" class="main-logo img-responsive" alt="Muvee Reviews" title="Muvee Reviews"></a></div><div class="col-lg-6 hidden-md text-center hidden-sm hidden-xs"></div><div class="col-lg-3 col-md-6 col-sm-7 hidden-xs"><div class="right-box"><button type="button" class="access-btn" data-toggle="modal" data-target="#enquirypopup">新增视频</button></div></div></div><!-- MENU --><div class="row home-mega-menu "><div class="col-md-12"><nav class="navbar navbar-default"><div class="navbar-header"><button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".js-navbar-collapse"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button></div><div class="collapse navbar-collapse js-navbar-collapse megabg dropshd "><ul class="nav navbar-nav"><li><a href="index.html">视频点播</a></li></ul><div class="search-block"><form><input type="search" placeholder="Search"></form></div></div><!-- /.nav-collapse --></nav></div></div><!-- CORE --><div class="row"><!-- SIDEBAR --><div class="col-lg-2 col-md-4 hidden-sm hidden-xs"></div><!-- HOME MAIN POSTS -->	<div class="col-lg-10 col-md-8"><section id="home-main"><h2 class="icon"><i class="fa fa-television" aria-hidden="true"></i>视频列表</h2><div class="row"><!-- ARTICLES --><div class="col-lg-9 col-md-12 col-sm-12"><div class="row auto-clear"><article class="col-lg-3 col-md-6 col-sm-4" v-for="video in videos"><!-- POST L size --><div class="post post-medium"><div class="thumbr"><a class="afterglow post-thumb" v-bind:href="'/video.html?id='+video.id" target="_blank"><span class="play-btn-border" title="Play"><i class="fa fa-play-circle headline-round" aria-hidden="true"></i></span><div class="cactus-note ct-time font-size-1"><span>02:02</span></div><img class="img-responsive" v-bind:src="video.image" alt="#" v-cloak></a></div><div class="infor"><h4><a class="title" href="#" v-cloak>{{video.name}}</a></h4><span class="posts-txt" title="Posts from Channel"><i class="fa fa-thumbs-up" aria-hidden="true"></i>20.895</span><div class="ratings"><i class="fa fa-star" aria-hidden="true"></i><i class="fa fa-star" aria-hidden="true"></i><i class="fa fa-star-half-o" aria-hidden="true"></i><i class="fa fa-star-o"></i><i class="fa fa-star-half"></i></div></div></div></article></div><div class="clearfix spacer"></div></div><!-- RIGHT ASIDE --><div class="col-lg-3 hidden-md col-sm-12 text-center top-sidebar"></div></div></section></div></div>
</div>
<!-- CHANNELS -->
<div id="channels-block" class="container-fluid channels-bg">
</div>
<!-- BOTTOM BANNER -->
</div>
<!-- FOOTER -->
<div id="footer" class="container-fluid footer-background"><div class="container"><footer><!-- SECTION FOOTER --><div class="row"><!-- SOCIAL --><div class="col-lg-3 col-md-3 col-sm-6 col-xs-12"><div class="row auto-clear"></div></div><!-- TAGS --><div class="col-lg-3 col-md-3 col-sm-6 col-xs-12"></div><!-- POST --><div class="col-lg-3 col-md-3 col-sm-6 col-xs-12"></div><!-- LINKS --><div class="col-lg-3 col-md-3 col-sm-6 col-xs-12"></div></div><div class="row copyright-bottom text-center"><div class="col-md-12 text-center"><a href="" class="footer-logo" title="Video Magazine Bootstrap HTML5 template"><img src="img/gwj_logo.png" class="img-responsive text-center" alt="Video Magazine Bootstrap HTML5 template"></a>	<p v-cloak>HAUE &copy; Author by Gaowenjun</p></div></div></footer></div>
</div>
<!-- MODAL -->
<div id="enquirypopup" class="modal fade in " role="dialog"><div class="modal-dialog"><!-- Modal content--><div class="modal-content row"><div class="modal-header custom-modal-header"><button type="button" class="close" data-dismiss="modal">×</button><h2 class="icon"><i class="fa fa-television" aria-hidden="true"></i>新增视频</h2></div><div class="modal-body"><form name="info_form" class="form-inline" action="/add" method="post" enctype="multipart/form-data"><div class="form-group col-sm-12"><input type="text" class="form-control" name="name" placeholder="输入视频名称"></div><div class="form-group col-sm-12"><input type="text" class="form-control" name="info" placeholder="输入视频简介"></div><div class="form-group col-sm-12"><input type="file" class="form-control" name="video" placeholder="选择视频文件"></div><div class="form-group col-sm-12"><input type="file" class="form-control" name="image" placeholder="选择封面图片"></div><div class="form-group col-sm-12"><button class="subscribe-btn pull-right" type="submit" title="Subscribe">上传</button></div></form></div></div></div>
</div>
</div>
</body><!-- JAVA SCRIPT -->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="js/jquery-1.12.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/lity.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>$(".nav .dropdown").hover(function() {$(this).find(".dropdown-toggle").dropdown("toggle");});
</script>
<script>let app = new Vue({el: '#myapp',data: {author: "Gaowenjun",videos:[]},methods : {get_allvideos : function() {$.ajax({url: "/video",type: "get",context: this,success : function(result, status, xhr){this.videos = result;}})}}});app.get_allvideos();
</script>
</html>


在这里插入图片描述

相关文章:

【从零到一,C++项目实战】CineShare++(基于C++的视频点播系统)

&#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系列 &#x1f308;Linux学习专栏&#xff1a; 南桥谈Linux &#x1f308;数据结构学习专栏&#xff1a; 数据结构杂谈 &#x1f308;数据…...

kaggle-ISIC 2024 - 使用 3D-TBP 检测皮肤癌-学习笔记

问题描述&#xff1a; 通过从 3D 全身照片 (TBP) 中裁剪出单个病变来识别经组织学确诊的皮肤癌病例 数据集描述&#xff1a; 图像临床文本信息 评价指标&#xff1a; pAUC&#xff0c;用于保证敏感性高于指定阈值下的AUC 主流方法分析&#xff08;文本&#xff09; 基于CatBoo…...

C语言深入解析 printf的底层源码实现

深入解析 printf 的底层源码实现 printf 是 C 标准库中最常用的函数之一&#xff0c;用于格式化输出字符串。它的底层实现复杂且高效&#xff0c;包含多个模块化的函数和机制。本文结合 GNU C Library&#xff08;glibc&#xff09;的源码&#xff0c;详细分析 printf 的实现原…...

IPhone14 Pro 设备详情

目录 产品宣传图内部图——后设备详细信息 产品宣传图 内部图——后 设备详细信息 信息收集于HubWeb.cn...

vim多文件操作如何同屏开多个文件

[rootxxx ~]# vimdiff aa.txt bb.txt cc.txt #带颜色比较的纵向排列打开的同屏多文件操作 示例&#xff1a; [rootxxx ~]# vimdiff -o aa.txt bb.txt cc.txt #带颜色比较的横向排列打开的同屏多文件操作 示例&#xff1a; [rootxxx ~]# vim -O aa.txt bb.txt c…...

18款炫酷烟花合集

系列专栏 《Python趣味编程》《C/C趣味编程》《HTML趣味编程》《Java趣味编程》 写在前面 Python、C/C、HTML、Java等4种语言实现18款炫酷烟花的代码。 Python Python烟花① 完整代码&#xff1a;Python动漫烟花&#xff08;完整代码&#xff09;-CSDN博客 ​ Python烟…...

服务器虚拟化技术详解与实战:架构、部署与优化

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 引言 在现代 IT 基础架构中&#xff0c;服务器虚拟化已成为提高资源利用率、降低运维成本、提升系统灵活性的重要手段。通过服务…...

01.双Android容器解决方案

目录 写在前面 一&#xff0c;容器 1.1 容器的原理 1.1.1 Namespace 1.1.2 Cgroups&#xff08;Control Groups&#xff09; 1.1.3 联合文件系统&#xff08;Union File System&#xff09; 1.2 容器的应用 1.2.1 微服务架构 1.2.2 持续集成和持续部署&#xff08;CI/C…...

C++封装红黑树实现mymap和myset和模拟实现详解

文章目录 map和set的封装map和set的底层 map和set的模拟实现insertiterator实现的思路operatoroperator- - map和set的封装 介绍map和set的底层实现 map和set的底层 一份模版实例化出key的rb_tree和pair<k,v>的rb_tree rb_tree的Key和Value不是我们之前传统意义上的key/…...

嵌入式知识点总结 Linux驱动 (一)-指令-常用Linux指令 GCC指令 GDB调试指令 驱动开发指令

针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.怎么查看当前进程&#xff1f;怎么执行退出&#xff1f;怎么查看当前路径&#xff1f; 2.ls命令执行有什么功能&#xff1f;可以带哪些参数&#xff1f; 3.创建目录用什么命令…...

数字化创新者如何利用开源2+1链动模式AI智能名片S2B2C商城小程序源码重塑市场地位

摘要&#xff1a;在数字化转型的浪潮中&#xff0c;数字化创新者正通过整合前沿技术&#xff0c;重塑行业格局&#xff0c;引领市场变革。本文深入探讨了开源21链动模式、AI智能名片以及S2B2C商城小程序源码等技术在数字化创新中的应用&#xff0c;旨在揭示这些技术如何助力企业…...

以用户为中心,优化 B 端界面设计

在数字化转型的进程中&#xff0c;B 端产品已成为企业高效运营的关键支撑&#xff0c;而 B 端界面设计则是决定其成败的核心要素。以用户为中心优化 B 端界面&#xff0c;不仅能提升员工操作体验&#xff0c;还能为企业运营注入强大动力。 以用户为中心&#xff0c;意味着深入洞…...

微服务学习-服务调用组件 OpenFeign 实战

1. OpenFeign 接口方法编写规范 1.1. 在编写 OpenFeign 接口方法时&#xff0c;需要遵循以下规范 1.1.1.1. 接口中的方法必须使用 RequestMapping、GetMapping、PostMapping 等注解声明 HTTP 请求的类型。 1.1.1.2. 方法的参数可以使用 RequestParam、RequestHeader、PathVa…...

使用国内镜像加速器解决 Docker Hub 拉取镜像慢或被屏蔽的问题

一、问题背景 Docker Hub 是 Docker 默认的镜像仓库&#xff0c;但由于网络限制&#xff0c;国内用户直接拉取镜像可能面临以下问题&#xff1a; 下载速度极慢&#xff08;尤其是大镜像&#xff09;。连接超时或完全被屏蔽&#xff08;部分网络环境&#xff09;。依赖国外源的…...

反向代理模块。。

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;将从服务器上得到的结果返回给客户端&#xff0c;此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说&#xff0c;反向代理就相当于…...

《十七》浏览器基础

浏览器&#xff1a;是安装在电脑里面的一个软件&#xff0c;能够将页面内容渲染出来呈现给用户查看&#xff0c;并让用户与网页进行交互。 常见的主流浏览器&#xff1a; 常见的主流浏览器有&#xff1a;Chrome、Safari、Firefox、Opera、Edge 等。 输入 URL&#xff0c;浏览…...

飞致云开源社区月度动态报告(2025年1月)

自2023年6月起&#xff0c;中国领先的开源软件公司飞致云以月度为单位发布《飞致云开源社区月度动态报告》&#xff0c;旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况&#xff0c;以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源运营数据概览&…...

【PyQt5】数据库连接失败: Driver not loaded Driver not loaded

报错内容如下&#xff1a; 可以看到目前所支持的数据库驱动仅有[‘QSQLITE’, ‘QMARIADB’, ‘QODBC’, ‘QODBC3’, ‘QPSQL’, ‘QPSQL7’] 我在网上查找半天解决方法未果&#xff0c;其中有一篇看评论反馈是可以使用的&#xff0c;但是PyQt5的版本有点低&#xff0c;5.12…...

Linux之内存管理前世今生(一)

一个程序&#xff08;如王者荣耀&#xff09;平常是存储在硬盘上的&#xff0c;运行时才把这个程序载入内存&#xff0c;CPU才能执行。 问题&#xff1a; 这个程序载入内存的哪个位置呢&#xff1f;载入内核所在的空间吗&#xff1f;系统直接挂了。 一、虚拟内存 1.1 内存分…...

【cran Archive R包的安装方式】

cran Archive R包的安装方式 添加链接描述 1.包被cran移除 2.包要求的R语言版本与你电脑上的版本不相符 ad archive包的网址或者是下载到工作目录下&#xff0c;ad等于文件名 install,packages(ad repos NULL)...

Python中容器类型的数据(上)

若我们想将多个数据打包并且统一管理&#xff0c;应该怎么办? Python内置的数据类型如序列(列表、元组等)、集合和字典等可以容纳多项数据&#xff0c;我们称它们为容器类型的数据。 序列 序列 (sequence) 是一种可迭代的、元素有序的容器类型的数据。 序列包括列表 (list)…...

2025美赛美国大学生数学建模竞赛A题完整思路分析论文(43页)(含模型、可运行代码和运行结果)

2025美国大学生数学建模竞赛A题完整思路分析论文 目录 摘要 一、问题重述 二、 问题分析 三、模型假设 四、 模型建立与求解 4.1问题1 4.1.1问题1思路分析 4.1.2问题1模型建立 4.1.3问题1样例代码&#xff08;仅供参考&#xff09; 4.1.4问题1样例代码运行结果&…...

OneDrive同步桌面 实现文件备份

Target 将笔记本桌面与OneDrive同步&#xff0c;当Desktop的文件发生变动时&#xff0c;自动更新到OneDrive。 取消旧的OneDrive账号与本机的关联 点击logo&#xff0c;弹出界面&#xff0c;点击设置。 随后打开OneDrive界面&#xff0c;点击“同步与备份”&#xff0c;“备…...

14.模型,纹理,着色器

模型、纹理和着色器是计算机图形学中的三个核心概念&#xff0c;用通俗易懂的方式来解释&#xff1a; 1. 模型&#xff1a;3D物体的骨架 通俗解释&#xff1a; 模型就像3D物体的骨架&#xff0c;定义了物体的形状和结构。 比如&#xff0c;一个房子的模型包括墙、屋顶、窗户等…...

用C++编写一个2048的小游戏

以下是一个简单的2048游戏的实现。这个实现使用了控制台输入和输出&#xff0c;适合在终端或命令行环境中运行。 2048游戏的实现 1.游戏逻辑 2048游戏的核心逻辑包括&#xff1a; • 初始化一个4x4的网格。 • 随机生成2或4。 • 处理玩家的移动操作&#xff08;上、下、左、…...

python -m pip和pip的主要区别

python -m pip和pip的主要区别在于它们与Python环境的关联方式和安装路径。‌ ‌与Python环境的关联方式‌&#xff1a; pip 是直接使用命令行工具来安装Python包&#xff0c;不指定特定的Python解释器。如果系统中存在多个Python版本&#xff0c;可能会导致安装的包被安装到…...

linux监控脚本+自动触发邮件发送

linux脚本 需求&#xff1a; CPU 负载&#xff1a;使用 uptime 命令&#xff0c;我们可以清楚地了解系统的 CPU 负载情况。这个命令会显示系统在过去 1 分钟、5 分钟和 15 分钟的平均负载。高负载可能意味着系统正在处理大量的任务&#xff0c;可能会导致性能下降或服务响应延迟…...

Mybaties缓存机制

Mybatis缓存机制 在 MyBatis 中&#xff0c;缓存机制是用来提高查询效率的一种方式。MyBatis 提供了两种内置的缓存机制&#xff1a;一级缓存和二级缓存。 1. 一级缓存&#xff08;Local Cache&#xff09; 一级缓存是基于 SqlSession 的。当你在同一个 SqlSession 中执行相…...

面试题-Java集合框架

前言 Java集合框架&#xff08;Java Collections Framework&#xff09;是Java平台提供的一套用于表示和操作集合的统一架构。它位于java.util包中&#xff0c;并且自Java 1.2&#xff08;也称为Java 2平台&#xff0c;标准版&#xff0c;即Java SE 2&#xff09;起成为Java平…...

半波整流和全波整流电路汇总及电路仿真

0 前言 0.1 引言 整流电路是将交流电(AC)转换为直流电(DC)的电路,广泛应用于电源设计、信号处理和电力电子等领域。整流电路的基本功能是将交流电的正半周或负半周转换为直流电,从而为电子设备提供稳定的直流电源。本文将详细介绍半波整流和全波整流电路的工作原理、电…...

04-机器学习-网页数据抓取

网络爬取&#xff08;Web Scraping&#xff09;深度指南 1. 网络爬取全流程设计 一个完整的网络爬取项目通常包含以下步骤&#xff1a; 目标分析&#xff1a; 明确需求&#xff1a;需要哪些数据&#xff08;如商品价格、评论、图片&#xff09;&#xff1f;网站结构分析&…...

豆包MarsCode:前缀和计算问题

问题描述 思路分析 问题理解 小S的任务是计算一个整数数组 nums 的前缀和。前缀和是指从数组开始到某个位置的所有元素的累加值&#xff0c;形成一个新数组。例如&#xff1a; 输入数组&#xff1a;nums [4, 5, 1, 6]前缀和数组&#xff1a;[4, 9, 10, 16] 4 49 4 510 …...

MySQL中InnoDB逻辑存储结构

在MySQL中&#xff0c;InnoDB是最常用的存储引擎之一&#xff0c;它具有高度的事务支持、行级锁、ACID特性以及自动崩溃恢复等特性。InnoDB的逻辑存储结构可以分为多个层次&#xff0c;下面是详细的解析。 1. 表空间 (Tablespace) InnoDB的物理存储结构以表空间为基础。表空间…...

如何提高新产品研发效率

优化研发流程、采用先进工具、提升团队协作、持续学习与改进&#xff0c;是提高新产品研发效率的关键。其中&#xff0c;优化研发流程尤为重要。通过简化流程&#xff0c;减少不必要的环节和复杂性&#xff0c;企业可以显著提升研发效率。例如&#xff0c;采用自动化测试工具和…...

可扩展架构:如何打造一个善变的柔性系统?

系统的构成:模块 + 关系 我们天天和系统打交道,但你有没想过系统到底是什么?在我看来,系统内部是有明确结构 的,它可以简化表达为: 系统 = 模块 + 关系 在这里,模块是系统的基本组成部分,它泛指子系统、应用、服务或功能模块。关系指模块 之间的依赖关系,简单…...

使用大语言模型在表格化网络安全数据中进行高效异常检测

论文链接 Efficient anomaly detection in tabular cybersecurity data using large language models 论文主要内容 这篇论文介绍了一种基于大型语言模型&#xff08;LLMs&#xff09;的创新方法&#xff0c;用于表格网络安全数据中的异常检测&#xff0c;称为“基于引导式提…...

BUUCTF 蜘蛛侠呀 1

BUUCTF:https://buuoj.cn/challenges 文章目录 题目描述&#xff1a;密文&#xff1a;解题思路&#xff1a;flag&#xff1a; 相关阅读 CTF Wiki Hello CTF NewStar CTF buuctf-蜘蛛侠呀 BUUCTF&#xff1a;蜘蛛侠呀 MISC&#xff08;时间隐写&#xff09;蜘蛛侠呀 题目描述&am…...

eVTOL的航空电子设备漫谈

电动垂直起降&#xff08;eVTOL&#xff09;&#xff0c;也统称为城市空中交通&#xff08;UAM&#xff09;&#xff0c;是民用航空平台发展的新方向之一。随着它们在市场上成为现实&#xff0c;它们将对所使用的航空电子设备有其自身的要求.. eVTOL概念 eVTOL领域的发展才刚…...

SpringCloudAlibaba 服务保护 Sentinel 项目集成实践

目录 一、简介1.1、服务保护的基本概念1.1.1、服务限流/熔断1.1.2、服务降级1.1.3、服务的雪崩效应1.1.4、服务的隔离的机制 1.2、Sentinel的主要特性1.3、Sentinel整体架构1.4、Sentinel 与 Hystrix 对比 二、Sentinel控制台部署3.1、版本选择和适配3.2、本文使用各组件版本3.…...

【深度学习|DenseNet-121】Densely Connected Convolutional Networks内部结构和参数设置

【深度学习|DenseNet-121】Densely Connected Convolutional Networks内部结构和参数设置 【深度学习|DenseNet-121】Densely Connected Convolutional Networks内部结构和参数设置 文章目录 【深度学习|DenseNet-121】Densely Connected Convolutional Networks内部结构和参数…...

【问题】Qt c++ 界面 lineEdit、comboBox、tableWidget.... SIGSEGV错误

蛋疼的错误集锦----今日分错误&#xff1a; 在主界面或者对话框的构造函数中&#xff0c;准备对一些对象赋值初始化值时&#xff0c;报了以上错误。&#xff01;&#xff01;&#xff01;&#xff01; 一脸懵逼的&#xff0c;各种确认&#xff0c;因为是最基础的赋值想不到错在…...

Hugging Face 推出最小体积多模态模型,浏览器运行成为现实!

1. SmolVLM 模型家族简介 1.1 什么是 SmolVLM-256M 和 SmolVLM-500M,它们为何如此重要? 在人工智能的多模态模型领域,如何在有限的计算资源下实现强大性能一直是一个重要的挑战。SmolVLM-256M 和 SmolVLM-500M 是最近推出的两款视觉语言模型,它们不仅突破了传统“大模型”…...

30289_SC65XX功能机MMI开发笔记(ums9117)

建立窗口步骤&#xff1a; 引入图片资源 放入图片 然后跑make pprj new job8 可能会有bug,宏定义 还会有开关灯报错&#xff0c;看命令行注释掉 接着把ture改成false 然后命令行new一遍&#xff0c;编译一遍没报错后 把编译器的win文件删掉&#xff0c; 再跑一遍虚拟机命令行…...

提供ZYNQ,MPSOC,RFSOC生成BOOT.BIN的小工具

如图&#xff1a; 这里提供了三种.bif&#xff0c;三种批处理.bat文件,一个bootgen.exe可执行文件和这个批处理文件运行是需要的动态库文件。 我们先看一下.bat文件&#xff0c;以BOOT_RFSOC为例&#xff1a; del temp\boot.bin bootgen -image output_rfsoc.bif -arch zynqmp…...

滤波电路汇总

0、前言 1. 引言 滤波电路是电子系统中不可或缺的组成部分,其主要功能是选择性地通过或衰减特定频率范围内的信号。在现代电子技术中,滤波电路广泛应用于信号处理、通信系统、音频设备、电源设计等多个领域。通过滤波,可以去除信号中的噪声和干扰,提高信号的质量和稳定性…...

springboot 动态配置定时任务

要在Spring Boot中动态配置定时任务&#xff0c;可以使用ScheduledTaskRegistrar类来实现。 首先&#xff0c;创建一个定时任务类&#xff0c;该类需要实现Runnable接口。例如&#xff1a; Component public class MyTask implements Runnable {Overridepublic void run() {/…...

Github 2025-01-25Rust开源项目日报Top10

根据Github Trendings的统计,今日(2025-01-25统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Python项目1Vue项目1JavaScript项目1Deno: 现代JavaScript和TypeScript运行时 创建周期:2118 天开发语言:Rust, JavaScript协议类型…...

【Linux网络编程】数据链路层

前言&#xff1a; 数据链路层非常简单&#xff0c;对于程序员来说&#xff0c;这里只需要大致了解即可&#xff0c;本篇文章不做重点说明。 数据链路层介绍 数据链路层是OSI位于物理层之上和网络层之下&#xff0c;这一层的报文叫做帧。它的主要任务是确保数据从一个节点可靠地…...

MongoDB 数据库备份和恢复全攻略

在当今数据驱动的时代&#xff0c;数据库的稳定运行和数据安全至关重要。MongoDB 作为一款流行的 NoSQL 数据库&#xff0c;以其灵活的文档模型和高扩展性备受青睐。然而&#xff0c;无论数据库多么强大&#xff0c;数据丢失的风险始终存在&#xff0c;因此掌握 MongoDB 的备份…...

一文了解性能优化的方法

背景 在应用上线后&#xff0c;用户感知较明显的&#xff0c;除了功能满足需求之外&#xff0c;再者就是程序的性能了。因此&#xff0c;在日常开发中&#xff0c;我们除了满足基本的功能之外&#xff0c;还应该考虑性能因素。关注并可以优化程序性能&#xff0c;也是体现开发能…...