「 视频点播项目」

视频点播

        该项目是一个共享的视频点播项目,用户通过浏览器对该网站进行访问,每个用户拥有增删查改该网站视频的权限。

技术与开发环境

开发环境:

系统:Ubuntu 20.04

编辑器:visual studio code(vscode)

编译器:gcc、g++

编译脚本:Makefile

所用技术:

C/C++、Linux、JSONCPP、MariaDB、httplib

JsonCpp

  JSON是⼀种数据交换格式,采⽤完全独⽴于编程语⾔的⽂本格式来存储和表⽰数据。JSON是一种开放标准的数据交换格式。JSON易于人类阅读和编写,同时也易于机器解析和生成。JSON独立于语言,这意味着很多编程语言都支持JSON格式的数据交换。JsonCpp主要包含三种类型的C++类 - value、reader、writer。value表示json对象和数组。reader用于 反序列化json字符串。writer用于序列化json字符串。
Json的数据类型包括:字符串、数组、对象、数字
  • 字符串:使⽤常规双引号 "" 括起来的表⽰⼀个字符串
  •  数组:使⽤中括号 [] 括起来的表⽰⼀个数组
  •  对象:使⽤花括号 {} 括起来的表⽰⼀个对象
  •  数字:包括整形和浮点型,直接使⽤
在本项目中使用最多的时序列化和反序列化,何为 序列化 反序列化
序列化: 将Value中的数据转换成JSON格式的数据
  5  #include <jsoncpp/json/json.h>
  6 
  7 int main()
  8 {
  9         const char* name="西小明";
 10         int age=19;
 11         float score[]={77.5,88,99};
 12         Json::Value val;
 13         val["姓名"]=name;
 14         val["年龄"]=age;
 15         val["成绩"].append(score[0]);
 16         val["成绩"].append(score[1]);
 17         val["成绩"].append(score[2]);
 18 
 19         Json::StreamWriterBuilder swb;
 20         std::unique_ptr<Json::StreamWriter>sw(swb.newStreamWriter());
 21 
 22         std::stringstream ss;
 23         int ret=sw->write(val,&ss);
 24         if(ret !=0)
 25         {
 26                 std::cout<<"write failed!\n";
 27                 return -1;
 28         }
 29         std::cout<<ss.str()<<std::endl;
 30         return 0;
 31 }

输出结果为:

由于JSON版本的问题,汉字编码出现一点点问题。

反序列化: 将JSON格式数据转成原始数据对象
  1  #include<iostream>
  2 #include<sstream>
  3 #include<string>
  4 #include<memory>
  5 #include<jsoncpp/json/json.h>
  6 
  7 void unserialize(const std::string &str)
  8 {
  9         Json::Value val;
 10         Json::CharReaderBuilder crb;
 11         std::unique_ptr<Json::CharReader>cr(crb.newCharReader());
 12         std::string err;
 13         bool ret=cr->parse(str.c_str(),str.c_str()+str.size(),&val,&err);
 14         if(ret==false)
 15         {
 16                 std::cout<<"parse failed!\n";
 17         }
 18         std::cout<<val["姓名"].asString()<<std::endl;
 19         std::cout<<val["年龄"].asInt()<<std::endl;
 20         int sz=val["成绩"].size();
 21         for(int i=0;i<sz;i++)
 22         {
 23                 std::cout<<val["成绩"][i]<<std::endl;
 24         }
 25 }
 26 int main()
 27 {
 28         std::string str=R"({"姓名":"小黑","年龄":18,"成绩":[66,77,65]})";
 29         unserialize(str);
    }

反序列化结果:

MariaDB

  MariaDB Server 是最流行的开源关系型数据库之一。它由 MySQL 的原始开发者制作,并保证保持开源。它是大多数云产品的一部分,也是大多数 Linux 发行版的默认配置。MariaDB 被设计为 MySQL 的直接替代产品,具有更多功能,新的存储引擎,更少的错误和更好的性能。

  Mysql 的 C 语⾔ API 接口
  初始化和链接
   
        //初始化接口
  •         MYSQL *mysql_init(MYSQL *mysql);
        //参数为空则动态申请句柄空间进⾏初始化
        //失败返回 NULL
        //连接 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,         unsignedlong client_flag);
        //mysql--初始化完成的句柄
        //host---连接的 mysql 服务器的地址
        //user---连接的服务器的⽤⼾名
        //passwd-连接的服务器的密码
        //db ----默认选择的数据库名称
        //port---连接的服务器的端⼝: 默认 0 3306 端⼝
        //unix_socket---通信管道⽂件或者 socket ⽂件,通常置 NULL
        //client_flag---客⼾端标志位,通常置 0
        //返回值:成功返回句柄,失败返回 NULL
        
操作数据库
        
  •         int mysql_select_db(MYSQL *mysql, const char *db)
        //mysql--初始化完成的句柄
        //db-----要切换选择的数据库名称
        //返回值:成功返回 0 , 失败返回⾮ 0
        //执⾏ sql 语句
  •         int mysql_query(MYSQL *mysql, const char *stmt_str)
        //mysql--初始化完成的句柄
        //stmt_str--要执⾏的 sql 语句
        //返回值:成功返回 0 , 失败返回⾮ 0
        //保存查询结果到本地
  •         MYSQL_RES *mysql_store_result(MYSQL *mysql)
        //mysql--初始化完成的句柄
        //返回值:成功返回结果集的指针, 失败返回 NULL
遍历结果集
  •         MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)
        //result--保存到本地的结果集地址
        //返回值:实际上是⼀个 char ** 的指针,将每⼀条数据做成了字符串指针数组 row[0]- 0
        row[1]-第 1
        //并且这个接⼝会保存当前读取结果位置,每次获取的都是下⼀条数据
释放结果集和关闭数据库
  •         void mysql_free_result(MYSQL_RES *result)
        //result--保存到本地的结果集地址
        //返回值: void
  •         void mysql_close(MYSQL *mysql)

httplib

  httplib 库,⼀个 C++11 单⽂件头的跨平台 HTTP/HTTPS 库。安装起来⾮常容易。只需包含httplib.h 在你的代码中即可。httplib 库实际上是⽤于搭建⼀个简单的 http 服务器或者客⼾端的库,这种第三⽅⽹络库,可以让我们免去搭建服务器或客⼾端的时间,把更多的精⼒投⼊到具体的业务处理中,提⾼开发效率。
  1. 创建HTTP服务器‌:可以使用httplib库创建一个HTTP服务器,用于处理客户端的HTTP请求并返回相应的HTTP响应。这对于开发Web应用程序或者提供Web服务非常有用。
  2. 创建HTTP客户端‌:可以使用httplib库创建一个HTTP客户端,用于向其他服务器发送HTTP请求并接收相应的HTTP响应。这对于与其他服务器进行通信或者获取远程资源非常有用。
  3. 处理HTTP请求和响应‌:httplib库提供了一些方便的API和工具,用于处理HTTP请求和响应,包括解析HTTP请求参数、处理POST请求、设置HTTP响应头部等功能。

httplib::Request  类是 httplib 库中用于表示 HTTP 请求的类。它包含了 HTTP 请求的各种属性,例如请求方法、URL、请求头、请求体等信息。

httplib::Request 类一般包含以下成员:

  • method: 表示 HTTP 请求的方法,如 “GET”、“POST”、“PUT” 等。
  • path: 表示 HTTP 请求的路径,即请求的资源在服务器上的位置。
  • headers: 表示 HTTP 请求的头部信息,通常包含诸如 Content-Type、Content-Length 等请求头。
  • body: 表示 HTTP 请求的主体内容,例如 POST 请求中包含的表单数据或 JSON 数据等。

httplib::Response类是 httplib 库中用于表示 HTTP 响应的类。它包含了 HTTP 响应的各种属性,例如状态码、响应头、响应体等信息。

httplib::Response 类一般包含以下成员:

  • version: 表示 HTTP 协议的版本,例如 “HTTP/1.1”。
  • status: 表示 HTTP 响应的状态码,例如 200、404、500 等。默认值为 -1。
  • reason: 表示 HTTP 响应状态码的原因短语,通常与状态码一起返回给客户端。
  • headers: 表示 HTTP 响应的头部信息,是一个键值对形式的容器,包含了各种元数据。
  • body: 表示 HTTP 响应的主体内容,即服务器返回给客户端的数据。
  • location: 用于重定向的目标 URL 地址,当服务器返回 3xx 状态码时,通常会包含重定向的目标地址。

httplib::Server类,主要的成员变量和方法

路由设置:

  • void Get(const std::string &pattern, Handler handler):设置对 GET 请求的处理函数。
  • void Post(const std::string &pattern, Handler handler):设置对 POST 请求的处理函数。
  • void Put(const std::string &pattern, Handler handler):设置对 PUT 请求的处理函数。
  • void Delete(const std::string &pattern, Handler handler):设置对 DELETE 请求的处理函数。
  • void Options(const std::string &pattern, Handler handler):设置对 OPTIONS 请求的处理函数。
  • Handler 是一个函数对象,通常为 std::function<void(const Request&, Response&)> 类型。

服务器控制:

  • bool listen(const char *host, int port, int socket_flags = 0):启动服务器,监听指定的主机和端口。
  • void stop():停止服务器。

中间件:

  • void set_logger(Logger logger):设置日志处理函数,用于记录请求和响应信息。
  • void set_error_handler(ErrorHandler handler):设置错误处理函数,用于处理 HTTP 错误。
  • Logger 是一个函数对象,通常为 std::function<void(const Request&, const Response&)> 类型。ErrorHandler 是一个函数对象,通常为 std::function<void(const Request&, Response&, int status_code)> 类型。

静态文件服务:

void set_mount_point(const char *mount_point, const char *dir):设置静态文件服务的挂载点和目录。
通过这个方法,服务器可以将某个 URL 路径映射到本地文件系统中的一个目录,从而提供静态文件服务。当用户请求静态资源的时候,则在指定的根目录下找,找到之后直接进行响应,不需要用户在外部进行额外的处理函数。

多线程操作

httplib 库中内置了一个简单的 TaskQueue 类,用于管理任务队列。这使得 httplib 能够在多线程环境下运行服务器并处理多个请求。TaskQueue 的实现隐藏在库内部,但我们可以通过使用 Server 类的 new_task_queue 方法来启用多线程支持。

工作流程

  1. 接受请求数据
  2. 进行解析,得到Request结构
  3. 检测映射表,根据请求的方法和资源路径查询有没有对应的处理函数。有,则调用,并且将Request和空Response对象传入。没有,则检测是否是静态资源。如果存在该静态资源,则直接返回;否则返回404页面。
  4. 当处理函数执行完毕之后的到一个填充完毕的Response对象。
  5. 根据Response对象的数据,组织http响应发送给客户端。
  6. 短连接则直接关闭,长连接等待超时后关闭。         

httplib库搭建简单服务器:首先将一个index.html文件放置在www文件夹下,然后书写服务端代码

  1 #include "./httplib.h"
  2 using namespace httplib;
  3 
  4 void HelloBit(const Request &req,Response &rsp)
  5 {
  6         rsp.body="HelloBit!";
  7         rsp.status=200;//忽略,默认httplib会加上一个200的状态码
  8 }
  9 void Numbers(const Request &req,Response &rsp)
 10 {
 11         //matches:存放正则表达式匹配的规则数据 /numbers/123 matches[0]="/numbers/123" matches[1]="123"
 12         std::string num=req.matches[1];
 13         rsp.set_content(num,"text/plain");//给正文类型  向rsp.body里边添加数据,并且设置Content-Type类型
 14         rsp.status=200;
 15 }
 16 void Multipart(const Request &req,Response &rsp)
 17 {
 18         if(req.has_file("file1")==false)
 19         {
 20                 rsp.status=400;
 21                 return ;
 22         }
 23         MultipartFormData file=req.get_file_value("file1");//字段
 24         std::cout<<file.filename<<std::endl;//区域文件名称
 25         std::cout<<file.content<<std::endl;//区域文件内容
 26         rsp.status=200;//打印
 27 }
 28 int main()
 29 {
            Server server;
 31         //设置一个静态资源根目录
 32         server.set_mount_point("/","./www");
 33         //添加请求-处理函数映射信息
 34         server.Get("/hi",HelloBit);
 35         //在正则表达式中:d表示数字,+表示匹配前边的字符一次或多次,()表示单独捕捉数据
 36         server.Get("/numbers/(\\d+)",Numbers);
 37         server.Post("/multipart",Multipart);//先是路径 再是函数进行处理
 38         server.listen("0.0.0.0",9090);
 39         return 0;
 40 }

项目实现工具类

文件实用工具类

  在视频点播系统中因为涉及到⽂件上传,需要对上传的⽂件进⾏备份存储,因此⾸先设计封装⽂件操 作类,这个类封装完毕之后,则在任意模块中对⽂件进⾏操作时都将变的简单化,将进行一下几个方面的实现:
  • 获取⽂件⼤⼩(属性)
  • 判断⽂件是否存在
  • 向⽂件写⼊数据
  • 从⽂件读取数据
  • 针对⽬录⽂件多⼀个创建⽬录

util.hpp

#ifndef __MY_UTIL__
#define __MY_UTIL__
#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <sys/stat.h>
namespace Util
{
    class FileUtil
    {
    private:
        std::string _name;//文件路径名
    public:
        FileUtil(const std::string name):_name(name){}
        //判断文件是否存在
        bool Exists();
        //获取文件的大小
        size_t Size();
        //读取文件数据到*body中
        bool GetContent(std::string *body);
        //向文件写入数据
        bool SetContent(const std::string& body);
        //针对目录时创建目录
        bool CreateDirectory();
    };
}

#endif

判断文件是否存在

 int access(const char *pathname,int mode)   是用来专门检测文件是否存在
 参数:

  •          pathname:表示要测试的文件的路径
  •          mode:表示测试的模式可能的值有:
  •          R_OK:是否具有读权限
  •          W_OK:是否具有可写权限
  •          X_OK:是否具有可执行权限
  •          F_OK:文件是否存在

返回值:若测试成功则返回0,否则返回-1

获取文件大小



获取文件数据向文件写入数据

针对文件创造目录

JSON实用工具类

序列化

反序列化

数据管理模块

在视频共享点播系统中,视频数据和图⽚数据都存储在⽂件中,⽽我们需要在数据库中管理用户上传的每个视频信息, 只是完成⼀个简单的视频信息表。
  • 视频ID
  • 视频名称
  • 视频描述信息
  • 视频⽂件的 url 路径(加上相对根⽬录实际上就是实际存储路径)
  • 视频封⾯图⽚的 URL 路径(只是链接,加上相对根⽬录才是实际的存储路径)

对该表要进行的操作有:

  • 增加视频
  • 删除视频
  • 更新视频
  • 查询所有视频
  • 查询单个视频
  • 模糊匹配查询视频
  视频信息在接⼝之间的 传递因为字段数量可能很多,因此使⽤ Json::Value 对象进⾏传递,同时也要满足多线程的需求,为数据安全,需要添加锁

data.hpp

#ifndef__MY_DATA__
#define__MY_DATA__
#include "util.hpp"
#include <mutex>
#include <cstdlib>
#include <mysql/mysql.h>
namespace aod {
 static MYSQL *MysqlInit();
 static void MysqlDestroy(MYSQL *mysql);
 static bool MysqlQuery(MYSQL *mysql, const std::string &sql);
 class TableVideo {
 private:
 MYSQL *_mysql;//⼀个对象就是⼀个客⼾端,管理⼀张表
 std::mutex _mutex;//防备操作对象在多线程中使⽤存在的线程安全 问题
 public:
 TableVideo();//完成mysql句柄初始化
 ~TableVideo();//释放msyql操作句柄
 bool Insert(const Json::Value &video);//新增-传⼊视频信息
 bool Update(int video_id, const Json::Value &video);//修改-传⼊视频
id,和信息
 bool Delete(const int video_id);//删除-传⼊视频ID
 bool SelectAll(Json::Value *videos);//查询所有--输出所有视频信息
 bool SelectOne(int video_id, Json::Value *video);//查询单个-输⼊视频
id,输出信息
 bool SelectLike(const std::string &key, Json::Value *videos);//模糊
匹配-输⼊名称关键字,输出视频信息
 };
}
#endif

初始化接口:

销毁数据库接口:

执行数据库接口:

数据库表的管理

增加视频:

更新视频:

删除视频:

查询所有视频:

查询单个视频:

模糊信息匹配查询:

一个对象就是一个客户端,管理一张表

网络通信接口设计

该项目使用的收httlib库搭建服务器,相对比较简单。网络通信接口,主要是我们定义什么样的请求,就是一个什么样的请求。

restful风格建立在http协议,其中定义了,GET方法是查询,POST方法是新增,PUT方法是修改,DELETE方法是删除,并且资源数据采用json、xml数据格式。

restful 认识:
REST Representational State Transfer 的缩写,⼀个架构符合 REST 原则,就称
它为 RESTful 架构,RESTful 架构可以充分的利⽤ HTTP 协议的各种功能,是 HTTP 协议的最佳实践,正⽂通常采⽤ JSON 格式,RESTful API 是⼀种软件架构⻛格、设计⻛格,可以让软件更加清晰,更简洁,更有层次,可维护性更好。
restful 使⽤五种 HTTP ⽅法,对应 CRUD( 增删改查 ) 操作
  • GET 表⽰查询获取
  • POST 对应新增
  • PUT 对应修改
  • DELETE 对应删除

业务处理模块设计

业务处理模块负责与客⼾端进⾏⽹络通信,接收客⼾端的请求,然后根据请求信息,明确客⼾端端⽤户的意图,进⾏业务处理,并进⾏对应的结果响应。
在视频点播项目
业务处理主要包含两⼤功能:
  • ⽹络通信功能的实现
  • 业务功能处理的 实现
业务处理模块所要完成的业务功能主要有:
  • 客户端的视频数据和信息上传
  • 客户端的视频列表展⽰(视频信息查询)
  • 客户端的视频观看请求(视频数据的获取)
  • 客户端的视频其他管理(修改,删除)功能

server.hpp

#ifndef__MY_SERVERE__
#define__MY_SERVERE__
#include "httplib.h"
#include "data.hpp"
namespace aod 
{
 #define WWW_ROOT "./www"
 #define VIDEO_ROOT "/video/"
 #define IMAGE_ROOT "/image/"
 //因为httplib基于多线程,因此数据管理对象需要在多线程中访问,为了便于访问定义全局变量
 TableVideo *table_video = NULL;
 //这⾥为了更加功能模块划分清晰⼀些,不使⽤lamda表达式完成,否则所有的功能实现集中到⼀
个函数中太过庞⼤
 class Server {
 private:
     int _port;//服务器的 监听端⼝
     httplib::Server _srv;//⽤于搭建http服务器
 private:
 //对应的业务处理接⼝
     static void Insert(const httplib::Request &req, httplib::Response&rsp);
     static void Update(const httplib::Request &req, httplib::Response &rsp);
     static void Delete(const httplib::Request &req, httplib::Response &rsp);
     static void GetOne(const httplib::Request &req, httplib::Response &rsp);
     static void GetAll(const httplib::Request &req, httplib::Response &rsp);
 public:
     Server(int port):_port(port);
     bool RunModule();//建⽴请求与处理函数的映射关系,设置静态资源根⽬录,启动
服务器,
 };
}

新增视频

            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,"reson":"上传的数据信息错误"})";
					rsp.set_header("Content-Type","application/json");
					return;
				}
				httplib::MultipartFormData name=req.get_file_value("name");//视频名称
				httplib::MultipartFormData info=req.get_file_value("info");//视频简介
				httplib::MultipartFormData video=req.get_file_value("video");//视频文件
				httplib::MultipartFormData image=req.get_file_value("image");//图片文件
			        //content就是视频内容	
				std::string video_name=name.content;
				std::string video_info=info.content;
				std::string root=WWWROOT;
				std::string video_path=root+VIDEO_ROOT+video_name+video.filename;//文件名
				std::string image_path=root+IMAGE_ROOT+video_name+image.filename;//./www/image/变形金刚

				//MultipartFormData{name,content_type,filename,content} content文件内容的数据
				//文件存储
				if(FileUtil(video_path).SetContent(video.content)==false)
				{
					rsp.status=500;
					rsp.body=R"({"result":false,"reson":"视频文件存储失败"})";
					rsp.set_header("Content-Type","application/json");
					return;
				}
				if(FileUtil(image_path).SetContent(image.content)==false)
				{
					rsp.status=500;
					rsp.body=R"({"result":false,"reson":"图片文件存储失败"})";
					rsp.set_header("Content-Type","application/json");
					return;

				}
				Json::Value video_json;
				video_json["name"]=video_name;
				video_json["info"]=video_info;
				video_json["video"]=VIDEO_ROOT +video_name+video.filename;// /video/变形金刚robot.mp4
				video_json["image"]=IMAGE_ROOT +video_name+image.filename;// /image/变形金刚robot.mp4
				if(tb_video->Insert(video_json)==false)
				{
					rsp.status=500;
					rsp.body=R"({"result":false,"reson":"数据库新增数据失败"})";
					rsp.set_header("Content-Type","application/json");
					return;
				}
				rsp.set_redirect("/index.html",303);
				return ;
			}

 视频信息更新

            static void Update(const httplib::Request &req,httplib::Response &rsp)
			{
				//1.获取要修改的视频信息 1.视频id 2.修改后的信息
				int video_id=std::stoi(req.matches[1]);
				Json::Value video;
				if(JsonUtil::UnSerialize(req.body,&video)==false)
				{
					rsp.status=400;
					rsp.body=R"({"result":false,"reson":"新的视频格式失败"})";
					rsp.set_header("Content-Type","application/json");
					return ;
				}
				//2.修改数据库数据
				if(tb_video->Update(video_id,video)==false)
				{
					rsp.status=500;
					rsp.body=R"({"result":false,"reson":"修改数据库信息失败"})";
					rsp.set_header("Content-Type","application/json");
					return;
				}
				return ;
			}

视频删除

            static void Delete(const httplib::Request &req,httplib::Response &rsp)
			{
				//1.获取要删除视频的ID
				int video_id=std::stoi(req.matches[1]);//获取ID
				//2.删除视频文件和图片文件
				Json::Value video;
				if(tb_video->SelectOne(video_id,&video)==false)
				{
					rsp.status=500;
					rsp.body=R"({"result":false,"reson":"不存在视频信息"})";
					rsp.set_header("Content-Type","application/json");
					return;
				}
				std::string root=WWWROOT;
				std::string video_path=root+video["video"].asString();
				std::string image_path=root+video["image"].asString();
				remove(video_path.c_str());
				remove(image_path.c_str());
				//3.删除数据库信息
				if(tb_video->Delete(video_id)==false)
				{
					rsp.status=500;
					rsp.body=R"({"result":false,"reson":"删除数据库信息失败"})";
					rsp.set_header("Content-Type","application/json");
					return;
				}
				return ;
			}

查询所有视频与模糊匹配

            static void SelectAll(const httplib::Request &req,httplib::Response &rsp)
			{
				// /video &   /video?search="关键字"
				bool select_flag=true;//默认查询所有
				std::string search_key;
				if(req.has_param("search")==true)
				{
					select_flag=false;//模糊匹配
					search_key=req.get_param_value("search");	
				}
				Json::Value videos;
				if(select_flag==true)
				{
					if(tb_video->SelectAll(&videos)==false)
					{
						rsp.status=500;
						rsp.body=R"({"result":false,"reson":"查询所有数据库失败"})";
						rsp.set_header("Content-Type","application/json");
						return;
					}
				}
				else
				{
					if(tb_video->SelectLike(search_key,&videos)==false)
					{
						rsp.status=500;
						rsp.body=R"({"result":false,"reson":"查询匹配数据库信息失败"})";
						rsp.set_header("Content-Type","application/json");
						return;
					}
				}
				JsonUtil::Serialize(videos,&rsp.body);
				rsp.set_header("Content-Type","application/json");
				return ;
			}

查询单个视频

            static void SelectOne(const httplib::Request &req,httplib::Response &rsp)
			{
				//1.获取视频的ID
				int video_id=std::stoi(req.matches[1]);//获取ID
				//2.在数据库中查询指定信息
				Json::Value video;
				if(tb_video->SelectOne(video_id,&video)==false)
				{
					rsp.status=500;
					rsp.body=R"({"result":false,"reson":"查询数据库单个失败"})";
					rsp.set_header("Content-Type","application/json");
					return;
				}
				//3.组织响应正文--json格式字符串
				JsonUtil::Serialize(video,&rsp.body);
				rsp.set_header("Content-Type","application/json");
			}

使用postman进行接口测试

新增视频测试:

查看数据库中是否有新增视频

查询所有视频:

查询单个视频:

模糊匹配:

修改视频:

删除视频:

数据库查询:

前端界面

主界面

实现功能:

  • 所有视频显示并可以播放
  • 新增视频按钮
  • 搜索框

主页面展示

点击播放按钮,跳转至播放页面

点击新增视频按钮,跳出一个界面

完成上述功能所需的函数:

<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: "dou",
			videos:[]
		},
		methods:{
			get_alivideos :function(){
				$.ajax({
					url:"/video",
					type:"get",
					context: this,
					success : function(result,status,xhr){
						this.videos=result;
					}
				})
			}
		}
	});
	app.get_alivideos();
</script>

</html>

播放页面

主要功能:

  • 播放视频与暂停
  • 修改视频
  • 一键删除视频

播放与暂停:

点击视频修改按钮:

点击删除视频按钮,就会一键删除

完成上述功能所需的函数:

<script>
   let app = new Vue({
         el: '#myapp',
         data: {
            author:"dou",
            video:{}
         },
         methods:
         {
            get_param: function (name) {
                  return decodeURIComponent((new RegExp('[?|&]' + name + '=' +'([^&;]+?)(&|#|;|$)').exec(location.href) || [, ""])[1].replace(/\+/g, '%20')) || null
               },
            get_video:function(){
               var id=this.get_param("id");
               $.ajax({
                  url:"/video/"+id,
                  type:"get",
                  context:this,
                  success :function(result,status,xhr)
                  {
                     this.video=result;
                  }
               })
            },
            update_video:function(){
               $.ajax({
                  url:"/video/"+this.video.id,
                  type:"put",
                  data:JSON.stringify(this.video),
                  context:this,
                  success :function(result,status,xhr)
                  {
                     alert("修改视频信息成功");
                     window.location.reload();
                  }
               })
            },
            delete_video:function(){
               $.ajax({
                  url:"/video/"+this.video.id,
                  type:"delete",
                  context:this,
                  success :function(result,status,xhr)
                  {
                     alert("删除视频成功");
                     window.location.href="/index.html";
                  }
               }) 
            }

         }
      });
      app.get_video();
</script>
</html>

项目总结

技术特点: http 服务器搭建, restful ⻛格接⼝设计, json 序列化,线程池,html+css+js 基础
项⽬模块:
  • 数据管理模块:基于 MYSQL 进⾏数据管理,封装数据管理类进⾏数据统⼀访问
  • 业务处理模块:基于 HTTPLIB 搭建 HTTP 服务器,使⽤ restful⻛格 进⾏接⼝设计处理客户端业务请求
  • 前端界⾯模块:基于基础的 HTML+CSS+JS 完成基于简单模板前端界⾯的修改与功能完成

总结:

  在本次项目中学习很多第三方库,并投入使用,也了解到网络传输中数据以什么样的格式是如何传送的,数据的存储,通过MariaDB存储数据,但视频和封面图片所占内存较大,存储在文件中,使用httplib搭建服务器,是本项目容易了很多,实现了前后端数据信息的交互。在本次项目中学习服务器的搭建遇到了一些困难,自己搭建的服务器稳定性和安全性不高,所以采用httplib第三方库。学习了一些简单的前端基础,对视频播放模板进行改造。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值