【项目】视频点播

打印 上一主题 下一主题

主题 1020|帖子 1020|积分 3060

一、项目介绍

1. 对视频点播体系的认识

搭建视频共享点播服务器,可以让所有人通过欣赏器访问服务器,实现视频的上传查看,以及管理并播放的功能。重要是完成服务器端的步调业务功能的实现以及前端访问界面 html 的编写,能够支持客户端欣赏器针对服务器上的所有视频举行操纵。
2. 服务端功能模块划分

该视频点播体系根本上包罗四个模块:数据管理、网络通信、业务处理、前端界面,其功能如下:


  • 数据管理模块:负责针对客户端上传的视频信息举行管理。
  • 网络通信模块:搭建网络通信服务器,实现与客户端通信。
  • 业务处理模块:针对客户端的各个请求举行对应业务处理并相应结果。
  • 前端界面模块:完成前端欣赏器上视频共享点播的各个 html 页面,在页面中支持增删改查以及观看功能。

二、环境搭建

2.1 升级GCC

由于在该项目中会引入很多第三方库,比如httplib库,该库就会要求gcc编译器必须是较新的版本。假如使用老版本的编译器要么编译不通过,要么就会运行报错。因此我们需要对gcc举行升级

2.2 安装JsonCpp库

JSON 是一种轻量级的数据交换格式。它可以代表数字、字符串、值的有序序列和名称/值的聚集对。
JsonCpp 是一个C++库,允许操纵 JSON 值,包括字符串的序列化和反序列化。它还可以生存反序列化/序列化步骤中的现有注释,方便
用于存储用户输入文件的格式。
  1. sudo yum install epel-release
  2. sudo yum install jsoncpp-devel
复制代码
安装好的JsonCpp存放在/usr/include/jsoncpp/json目次下:

2.3 引入httplib库

cpp-httplib 是个开源的库,是一个c++封装的http库,使用这个库可以在linux、windows平台下完成http客户端、http服务端的搭建,这是一个多线程“壅闭”HTTP 库。使用起来非常方便,只需要包罗头文件httplib.h即可。源码地址
获取httplib库:
  1. git clone https://github.com/yhirose/cpp-httplib.git
复制代码
2.4 MySQL数据库

此次项目的开辟我们使用的数据库是MariaDB,MariaDB数据库管理体系是MySQL的一个分支,重要由开源社区在维护,接纳GPL授权允许 MariaDB的目的是完全兼容MySQL,包括API和下令行,使之能轻松成为MySQL的代替品。
以下是安装MariaDB数据库的步骤:


  • 安装 MariaDB 服务
  1. sudo yum install -y mariadb-server
复制代码


  • 安装 MariaDB 下令行客户端
  1. sudo yum install -y mariadb
复制代码


  • 安装 MariaDB C library
  1. sudo yum install -y mariadb-libs
复制代码
其实 MariaDB 的服务中包罗了客户端和相关的C语言接口,因此只需要安装 mariadb-server即可。


  • 安装 MariaDB 开辟包
  1. sudo yum install -y mariadb-devel
复制代码
安装完成后,以下是启动数据库的下令:


  • 启动服务
  1. systemctl start mariadb
复制代码
执行该下令启动服务之后,假如重启大概关机了,下次还需要重新启动,可以执行下列指令设置开启自启。


  • 设置服务开机自启
  1. systemctl enable mariadb
复制代码


  • 查看服务状态
  1. systemctl status mariadb
复制代码
启动服务成功后,我们可以看到MariaDB 的状态是 active(running) 
然后可以测试毗连数据库:


  • 使用下令行客户端尝试毗连
  1. mysql -uroot
复制代码
更改配置:
为了在使用数据库时支持中文,还需要举行以下配置。


  • 在 /etc/my.cnf.d/client.cnf文件的 [client]下添加default-character-set = utf8。



  • 在 /etc/my.cnf.d/mysql-client.cnf文件下的[mysql]下添加default-character-set = utf8。 



  • 在 /etc/my.cnf.d/server.cnf下的[mysqld]添加以下语句
  1. collation-server = utf8_general_ci
  2. init-connect = 'SET NAMES utf8'
  3. character-set-server = utf8
  4. sql-mode = TRADITIONAL
复制代码

三、第三方库的认识

3.1 认识JsonCpp

首先认识Json:
Json 是一种数据交换格式,接纳完全独立于编程语言的文本格式来存储和表示数据。
比方:使用Json来表示张三同砚的门生信息。
  1. const char* name1 = "张三";
  2. int age1 = 18;
  3. float scores1[3] = {60.0, 59.5, 61.0};
  4. const char* name2 = "李四";
  5. int age2 = 19;
  6. float scores2[3] = {69.0, 58.5, 64.0};
  7. // Json这种数据交换格式就是将这样的多种数据对象封装成一个字符串:
  8. [
  9.         {
  10.                 "姓名" : "张三",
  11.                 "年龄" : 18,
  12.                 "成绩" : [860.0, 59.5, 61.0]
  13.         },
  14.         {
  15.                 "姓名" : "李四",
  16.                 "年龄" : 19,
  17.                 "成绩" : [69.0, 58.5, 64.0]
  18.         },
  19. ]
复制代码
Json可以封装的数据对象可以是:对象,数组,字符串,数字等等。
认识JsonCpp:
JsonCpp 库用于实现 Json 格式的序列化和反序列化,完成将多个数据对象组织成为 Json 格式字符串,以及将 Json
格式字符串剖析得到多个数据对象的功能。
此中重要借助三个类以及对于的几个成员函数完成的。
Json 数据对象:
  1. class Json::Value{
  2.         Value &operator=(const Value &other); // Value重载了[]和=,因此所有的赋值和获取数据都可以通过
  3.         Value& operator[](const std::string& key);// 简单的方式完成 val["姓名"] = "小明";
  4.         Value& operator[](const char* key);
  5.         Value removeMember(const char* key);// 移除元素
  6.         const Value& operator[](ArrayIndex index) const; // val["成绩"][0]
  7.         Value& append(const Value& value);// 添加数组元素val["成绩"].append(88);
  8.         ArrayIndex size() const;// 获取数组元素个数 val["成绩"].size();
  9.         std::string asString() const;// 转string string name = val["name"].asString();
  10.         const char* asCString() const;// 转char* char *name = val["name"].asCString();
  11.         Int asInt() const;// 转int int age = val["age"].asInt();
  12.         float asFloat() const;// 转float
  13.         bool asBool() const;// 转 bool
  14. }
复制代码
Json序列化类:
  1. // 建议低版本使用
  2. class JSON_API Writer {
  3.         virtual std::string write(const Value& root) = 0;
  4. }
  5. class JSON_API FastWriter : public Writer {
  6.         virtual std::string write(const Value& root);
  7. }
  8. class JSON_API StyledWriter : public Writer {
  9.         virtual std::string write(const Value& root);
  10. }
  11. // 建议较高版本使用,如果用低版本接口可能报错
  12. class JSON_API StreamWriter {
  13.         virtual int write(Value const& root, std::ostream* sout) = 0;
  14. }
  15. class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
  16.         virtual StreamWriter* newStreamWriter() const;
  17. }
复制代码
Json反序列化类:
  1. // 低版本用起来更简单
  2. class JSON_API Reader {
  3.         bool parse(const std::string& document, Value& root, bool collectComments = true);
  4. }
  5. // 高版本更推荐
  6. class JSON_API CharReader {
  7.         virtual bool parse(char const* beginDoc, char const* endDoc,
  8.         Value* root, std::string* errs) = 0;
  9. }
  10. class JSON_API CharReaderBuilder : public CharReader::Factory {
  11.         virtual CharReader* newCharReader() const;
  12. }
复制代码
3.2 JsonCpp实现序列化

  1. #include <iostream>
  2. #include <memory>
  3. #include <sstream>
  4. #include <string>
  5. #include <jsoncpp/json/json.h>
  6. int main()
  7. {
  8.         const char* name = "张三";
  9.         int age = 18;
  10.         float scores[3] = {60.0, 59.5, 61.0};
  11.         Json::Value value;
  12.         value["姓名"] = name;
  13.         value["年龄"] = age;
  14.         value["成绩"].append(scores[0]);
  15.         value["成绩"].append(scores[1]);
  16.         value["成绩"].append(scores[2]);
  17.        
  18.         Json::StreamWriterBuilder swb;
  19.         std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
  20.        
  21.         std::stringstream ss;
  22.         int ret = sw->write(value, &ss);
  23.         if(ret != 0)
  24.         {
  25.                  std::cout << "write falied!" << std::endl;
  26.                  return -1;
  27.         }
  28.        
  29.         std::cout << ss.str() << std::endl;
  30.         return 0;
  31. }
复制代码

3.3 JsonCpp实现反序列化

  1. #include <iostream>
  2. #include <memory>
  3. #include <sstream>
  4. #include <string>
  5. #include <jsoncpp/json/json.h>
  6. int main()
  7. {
  8.     std::string str = R"({"姓名":"李四", "年龄":19, "成绩":[69.0, 58.5, 64.0]})";
  9.     Json::Value root;
  10.     Json::CharReaderBuilder crb;
  11.     std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
  12.     std::string err;
  13.     cr->parse(str.c_str(), str.c_str() + str.size(), &root, &err);
  14.     std::cout << root["姓名"].asString() << std::endl;
  15.     std::cout << root["年龄"].asInt() << std::endl;
  16.     int sz = root["成绩"].size();
  17.     for (int i = 0; i < sz; ++i)
  18.         std::cout << root["成绩"][i].asFloat() << std::endl;
  19.    
  20.     for(auto it = root["成绩"].begin(); it != root["成绩"].end(); ++it)
  21.         std::cout << it->asString() << std::endl;
  22.    
  23.     return 0;
  24. }
复制代码

3.4 认识MySQL数据库的API

详细参考这篇。
MySQL:函数式编程毗连MySQL库_mysqlconnection.h mysql.h-CSDN博客
编译指令:
  1. gcc -o mysql_test mysql_test.c -L/usr/lib64/mysql -lmysqlclient
复制代码
注意在举行编译的时候需要使用-L指定mysqlclient动态库所在的路径,因为它不是之间存在于/usr/lib64/路径下的。


  • 向数据库里面增长数据
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <mysql/mysql.h>
  5. //添加数据
  6. int add(MYSQL* mysql)
  7. {
  8.     const char* sql = "insert into test_tb values (null, 18, '张三', 61.5)";
  9.     int ret = mysql_query(mysql, sql);
  10.     if(ret != 0)
  11.     {
  12.         printf("query %s failed, error massage: %s\n", sql, mysql_error(mysql));
  13.             return -1;
  14.     }
  15.     return 0;
  16. }
  17. int main()
  18. {
  19.         //初始化操作句柄
  20.         MYSQL* mysql = mysql_init(NULL);
  21.         if(NULL == mysql)
  22.         {
  23.                 printf("init mysql handle failed!\n");
  24.                 return -1;
  25.         }
  26.        
  27.         //连接mysql服务器
  28.         if (mysql_real_connect(mysql, "127.0.0.1", "root", "", "test_db", 0, NULL, 0) == NULL)
  29.         {
  30.                 printf("mysql connect failed!\n");
  31.                 return -1;
  32.         }
  33.        
  34.         //设置客户端字符集
  35.         mysql_set_character_set(mysql, "utf8");
  36.        
  37.         add(mysql);
  38.        
  39.         //关闭句柄
  40.         mysql_close(mysql);
  41.         return 0;
  42. }
复制代码


  • 修改数据库中的数据
  1. //修改
  2. int mod(MYSQL* mysql)
  3. {
  4.     const char* sql = "update test_tb set name = '李四' where id = 2";
  5.     int ret = mysql_query(mysql, sql);
  6.     if(ret != 0)
  7.     {
  8.         printf("query %s failed, error massage: %s\n", sql, mysql_error(mysql));
  9.         return -1;
  10.     }
  11.     return 0;
  12. }
复制代码


  • 删除数据库中的数据
  1. //删除
  2. int del(MYSQL* mysql)
  3. {
  4.     const char* sql = "delete from test_tb where name='张三'";
  5.     int ret = mysql_query(mysql, sql);
  6.     if(ret != 0)
  7.     {
  8.         printf("query %s failed, error massage: %s\n", sql, mysql_error(mysql));
  9.         return -1;
  10.     }
  11.     return 0;
  12. }
复制代码


  • 查询数据库中的数据
  1. //查询
  2. int get(MYSQL* mysql)
  3. {
  4.     const char* sql ="select * from test_tb";
  5.     int ret = mysql_query(mysql, sql);
  6.     if(ret != 0)
  7.     {
  8.         printf("query %s failed, error massage: %s\n", sql, mysql_error(mysql));
  9.         return -1;
  10.     }
  11.     //保存结果集到本地
  12.     MYSQL_RES* result = mysql_store_result(mysql);
  13.     if(NULL == result)
  14.     {
  15.         printf("mysql store result failed! error message: %s\n", mysql_error(mysql));
  16.         return -1;
  17.     }
  18.     //获取结果集的行数
  19.     int row = mysql_num_rows(result);
  20.     //获取结果集的列数
  21.     int col = mysql_num_fields(result);
  22.     printf("%10s%10s%10s%10s\n", "ID", "姓名", "年龄", "成绩");
  23.     for(int i = 0; i < row; ++i)
  24.     {
  25.         //获取当前行的数据
  26.         MYSQL_ROW row_data = mysql_fetch_row(result);
  27.         for(int j = 0; j < col; ++j)
  28.         {
  29.             printf("%10s", row_data[j]);
  30.         }
  31.         printf("\n");
  32.     }
  33.     //释放结果集
  34.     mysql_free_result(result);
  35.     return 0;
  36. }
复制代码
3.6 认识httplib库

httplib库是一个基于C++11的跨平台的HTTP/HTTPS库,它的安装非常简单,只需要将httplib.h包罗在代码中即可。
httplib库实际上是用于搭建一个简单的HTTP服务器大概客户端的库,使用这种第三方网络库,可以帮助我们省去本身搭建服务器大概客户端的时间,把更多的精力投入到详细的业务处理当中,进步开辟的服从。
以下是对httplib库实现的简单分析,该库中重要包罗四个类:发送请求Request类,相应数据Response类,服务端Server类,客户端Client 类。
发送请求Request类的组成:
  1. namespace httplib
  2. {
  3.         struct MultipartFormData {
  4.         std::string name;
  5.         std::string content;
  6.         std::string filename;
  7.         std::string content_type;
  8.         };
  9.        
  10.         using MultipartFormDataItems = std::vector<MultipartFormData>;
  11.         struct Request
  12.         {
  13.                 std::string method;//存放请求方法
  14.                 std::string path;//存放请求资源路径
  15.                 Headers headers;//存放头部字段的键值对map
  16.                 std::string body;//存放请求正文
  17.                 // for server
  18.                 std::string version;//存放协议版本
  19.                 Params params;//存放url中查询字符串 key=val&key=val的 键值对map
  20.                 MultipartFormDataMap files;//存放文件上传时,正文中的文件信息
  21.                 Ranges ranges;
  22.                 bool has_header(const char *key) const;//判断是否有某个头部字段
  23.                 std::string get_header_value(const char *key, size_t id = 0) const;//获取头部字段值
  24.                 void set_header(const char *key, const char *val);//设置头部字段
  25.                 bool has_file(const char *key) const;//文件上传中判断是否有某个文件的信息
  26.                 MultipartFormData get_file_value(const char *key) const;//获取指定的文件信息
  27.         };
  28. }
复制代码
相应数据Response类:
  1. namespace httplib
  2. {
  3.         struct Response
  4.         {
  5.                 std::string version;//存放协议版本
  6.                 int status = -1;//存放响应状态码
  7.                 std::string reason;
  8.                 Headers headers;//存放响应头部字段键值对的map
  9.                 std::string body;//存放响应正文
  10.                 std::string location; // Redirect location重定向位置
  11.                 void set_header(const char *key, const char *val);//添加头部字段到headers中
  12.                 void set_content(const std::string &s, const char *content_type);//添加正文到body中
  13.                 void set_redirect(const std::string &url, int status = 302);//设置全套的重定向信息
  14.         };
  15. }
复制代码
服务端Server类:
  1. namespace httplib
  2. {
  3.         class Server
  4.         {
  5.                 using Handler = std::function<void(const Request &, Response &)>;//函数指针类型
  6.                 using Handlers = std::vector<std::pair<std::regex, Handler>>;//存放请求-处理函数映射
  7.                 std::function<TaskQueue *(void)> new_task_queue;//线程池
  8.                 Server &Get(const std::string &pattern, Handler handler);//添加指定GET方法的处理映射
  9.                 Server &Post(const std::string &pattern, Handler handler);
  10.                 Server &Put(const std::string &pattern, Handler handler);
  11.                 Server &Patch(const std::string &pattern, Handler handler);
  12.                 Server &Delete(const std::string &pattern, Handler handler);
  13.                 Server &Options(const std::string &pattern, Handler handler);
  14.                 bool listen(const char *host, int port, int socket_flags = 0);//开始服务器监听
  15.                 bool set_mount_point(const std::string &mount_point, const std::string &dir,
  16.                 Headers headers = Headers());//设置http服务器静态资源根目录
  17.         };
  18. }
复制代码
客户端Client 类:
  1. namespace httplib
  2. {
  3.         class Client
  4.         {
  5.                 //创建client
  6.                 Client(host,port);
  7.                 Get()
  8.                 Post()
  9.                 Put()
  10.                 ...
  11.         };
  12. }
复制代码
3.7 使用httplib库搭建简单的服务器

前端代码样例:
  1. <html>
  2.     <head>
  3.          <meta content="text/html; charset=utf-8" http-equiv="content-type" />
  4.     </head>
  5.     <body>
  6.         <h1>Hello HTTP</h1>
  7.         <form action="/multipart" method="post" enctype="multipart/form-data">
  8.             <input type="file" name="file1">
  9.             <input type="submit" value="上传">
  10.         </form>
  11.     </body>
  12. </html>
复制代码
这段代码是一个简单的HTML页面,包括一个标题"Hello HTTP"以及一个表单,允许用户上传文件。
在表单中,使用了HTTP POST方法并将enctype属性设置为multipart/form-data。这是因为表单旨在上传文件,需要这种类型的编码。文件输入字段使用input标签创建,类型属性设置为file,名称属性设置为file1。最后,使用input标签创建一个提交按钮,类型属性设置为submit,值属性设置为"上传"。
当用户单击提交按钮时,将拟定的表单数据,包括上传的文件,一起发送到表单标签中指定的服务器的文件中,即/multipart。
以下是使用httplib库实现的简单服务端代码:
  1. #include "./httplib.h"
  2. #include <iostream>
  3. using namespace httplib;
  4. void Handler(const Request &req, Response& rsp)
  5. {
  6.     rsp.body = "Hello World!";
  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");
  14.     rsp.status = 200;
  15. }
  16. void Multipart(const Request &req, Response& rsp)
  17. {   if(req.has_file("file1") == false)
  18.     {
  19.         rsp.status = 400;
  20.         return ;
  21.     }
  22.     MultipartFormData file =req.get_file_value("file1");
  23.     std::cout << file.filename << std::endl; //区域文件名称
  24.     std::cout << file.content << std::endl; //区域文件内容
  25. }
  26. int main()
  27. {
  28.     Server server;
  29.     //设置一个静态资源根目录---为当前的www目录
  30.     server.set_mount_point("/", "./www");
  31.     //添加请求---处理函数映射信息
  32.     server.Get("/hi", Handler);
  33.     //正则表达式中:\d表示数字,+表示匹配前面的字符一次或多次,()表示单独捕捉数据 /numbers/123
  34.     server.Get("/numbers/(\\d+)", Numbers);
  35.    
  36.     server.Post("/multipart", Multipart);
  37.    
  38.     server.listen("0.0.0.0", 8080);
  39.     return 0;
  40. }
复制代码
此中各个函数的作用:


  • Handler:回调函数,用于处理服务器的/hi路径的GET请求。当客户端向服务器发起GET请求而且请求路径是/hi时,该函数将被调用。并将相应的正文设置成 " Hello World!",相应状态设置为200。
  • Numbers:回调函数,用于处理服务器的/numbers/xxx路径的GET请求,此中xxx表示数字。当客户端向服务器发起GET请求而且请求路径是/numbers/xxx时,该函数将被调用。而且从请求对象的matches属性中提取数字并将其作为相应的正文,然后将相应状态码设置为200。
  • Multipart:回调函数,用于处理服务器的/multipart路径的POST请求。当客户端向服务器发起POST请求而且请求路径是/multipart时会调用该函数。该函数会检查请求是否包罗file1的文件,假如不存在,则将相应状态码设置为400。假如文件存在则将文件名和文件内容输出到控制台。
  • 在主函数中,服务器被创建并配置三个请求处理函数:Handler、Numbers、Multipart,而且指定服务端静态资源根目次为./www。然后监听8080的端口号等候客户端毗连。
四、服务端工具类的实现

4.1 文件工具类的计划

在视频点播体系中会涉及到文件的上传,需要对上传的文件举行备份存储,因此首先计划封装一个文件操纵类,将这个类封装完成后,则在恣意模块中对文件举行操纵时都将得到简化。
该类重要涉及到的功能是:获取文件的巨细、判定文件是否存在、向文件中写入数据、从文件中读取数据、针对目的文件创建目次。详细实现如以下代码:
  1. #include <iostream>
  2. #include <fstream>
  3. #include <string>
  4. #include <unistd.h>
  5. #include <sys/stat.h>
  6. namespace aod
  7. {
  8.   class FileUtil
  9.   {
  10.       private:
  11.           std::string _name; //文件路径名称
  12.       public:
  13.           FileUtil(const std::string& name):_name(name){}
  14.             
  15.           //判断文件是否存在
  16.           bool Exists()
  17.           {
  18.               int ret = access(_name.c_str(), F_OK); //access的第一个参数是文件名,第二个参数如果传入F_OK用于判断文件是否存在
  19.               if(ret != 0)
  20.               {
  21.                   std::cout << "file is not exist!" << std::endl;
  22.                   return false;
  23.               }
  24.               return true;
  25.           }
  26.          
  27.           //获取文件大小
  28.           size_t Size()
  29.           {
  30.               if(this->Exists() == false)
  31.               {
  32.                   return 0;
  33.               }
  34.               //stat接口用于获取文件属性,结构体struct stat中的st_size就是文件大小
  35.               struct stat st;
  36.               int ret = stat(_name.c_str(), &st);
  37.               if(ret != 0)
  38.               {
  39.                   std::cout << "get file size failed!" << std::endl;
  40.                   return 0;
  41.               }
  42.               return st.st_size;
  43.           }
  44.       
  45.           //获取文件内容
  46.           bool GetContent(std::string* content)
  47.           {
  48.               std::ifstream ifs;
  49.               ifs.open(_name.c_str(), std::ios::binary);
  50.               if(ifs.is_open() == false)
  51.               {
  52.                   std::cout << "open file failed!" << std::endl;
  53.                   return false;
  54.               }
  55.               size_t flen = this->Size();
  56.               content->resize(flen);
  57.               ifs.read(&(*content)[0], flen);
  58.               if(ifs.good() == false)
  59.               {
  60.                   std::cout << "read file content failed!" << std::endl;
  61.                   return false;
  62.               }
  63.               ifs.close();
  64.               return true;
  65.           }
  66.       
  67.           //向文件中写入内容
  68.           bool SetContent(const std::string& content)
  69.           {
  70.               std::ofstream ofs;
  71.               ofs.open(_name.c_str(), std::ios::binary);
  72.               if(ofs.is_open() == false)
  73.               {
  74.                   std::cout << "open file failed" << std::endl;
  75.                   return false;
  76.               }
  77.               
  78.               ofs.write(content.c_str(), content.size());
  79.               if(ofs.good() == false)
  80.               {
  81.                   std::cout << "write file content failed!" << std::endl;
  82.                   return false;
  83.               }
  84.               ofs.close();
  85.               return true;
  86.           }
  87.           //根据文件名称创建目录
  88.           bool CreateDirectory()
  89.           {
  90.               if(this->Exists())
  91.                   return true;
  92.               
  93.              int ret = mkdir(_name.c_str(), 0777);
  94.              if(ret != 0)
  95.              {
  96.                 std::cout << "create directory failed!" << std::endl;
  97.                 return false;
  98.              }
  99.              return true;
  100.           }
  101.   };
  102. }
  103. #endif
复制代码
【说明】


  • 在判定文件是否存在时使用的接口是access,调用成功返回 0 ,失败则返回 -1 ,其界说如下:
  1. #include <unistd.h>
  2. int access(const char *path, int amode);
复制代码
此中path是文件路径名称,amode用于指定access函数的作用,其取值如下:
  1. F_OK 值为0,判断文件是否存在
  2. X_OK 值为1,判断对文件是可执行权限
  3. W_OK 值为2,判断对文件是否有写权限
  4. R_OK 值为4,判断对文件是否有读权限
  5. 注:后三种可以使用或“|”的方式,一起使用,如W_OK|R_OK
复制代码


  • 在获取文件巨细的函数中使用了一个接口stat,其功能是获取文件的属性,调用成功返回 0 ,失败则返回 - 1,界说如下:
  1. #include <sys/stat.h>
  2. int stat(const char *path, struct stat *buf);
复制代码
此中path代表文件的路径,struct stat是一个描述文件的布局体,其界说如下:
  1. struct stat {
  2.     dev_t     st_dev;     /* ID of device containing file */
  3.     ino_t     st_ino;     /* inode number */
  4.     mode_t    st_mode;    /* protection */
  5.     nlink_t   st_nlink;   /* number of hard links */
  6.     uid_t     st_uid;     /* user ID of owner */
  7.     gid_t     st_gid;     /* group ID of owner */
  8.     dev_t     st_rdev;    /* device ID (if special file) */
  9.     off_t     st_size;    /* total size, in bytes */
  10.     blksize_t st_blksize; /* blocksize for file system I/O */
  11.     blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
  12.     time_t    st_atime;   /* time of last access */
  13.     time_t    st_mtime;   /* time of last modification */
  14.     time_t    st_ctime;   /* time of last status change */
  15. };
复制代码


  • 在创建文件目次时使用的函数是mkdir,调用成功返回 0, 失败则返回 -1,其界说如下:
  1. #include <sys/stat.h>
  2. int mkdir(const char *path, mode_t mode);
复制代码
4.2 Json 工具类的计划

Json工具类包罗的功能有两个,一个是将Json::Value对象序列化成为一个字符串,另一个是将字符串反序列化成为Json::Value对象。详细实现代码如下:
  1.   //Json工具类
  2.   class JsonUtil
  3.   {
  4.       public:
  5.           //序列化
  6.           static bool Serialize(const Json::Value& root, std::string* str)
  7.           {
  8.               Json::StreamWriterBuilder swb;
  9.               std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
  10.               
  11.               std::stringstream ss;
  12.               int ret = sw->write(root, &ss);
  13.               if(ret != 0)
  14.               {
  15.                   std::cout << "Serialize failed!" << std::endl;
  16.                   return false;
  17.               }
  18.               *str = ss.str();
  19.               return true;
  20.           }
  21.          
  22.           //反序列化
  23.           static bool Deserialize(const std::string& str, Json::Value* root)
  24.           {
  25.               Json::CharReaderBuilder crb;
  26.               std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
  27.               
  28.               std::string err;
  29.               bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);
  30.               if(ret == false)
  31.               {
  32.                   std::cout << "Deserialize failed! error message: " << err << std::endl;
  33.                   return false;
  34.               }
  35.               return true;
  36.           }
  37.   };
复制代码
五、数据管理模块的实现

5.1 视频数据表的计划

在视频点播体系中,视频数据和图片数据都存储在文件中,所有需要使用数据库来管理用户上传的每个视频的属性信息。这里只需要创建一个简单的视频信息表即可,其属性如下:
   

  • 视频ID
  • 视频名称
  • 视频描述信息
  • 视频文件的URL路径 (加上静态资源根目次就是实际存储路径)
  • 数据封面图片的URL路径 (加上静态资源根目次就是实际存储路径)
  数据库的创建代码如下:
  1. drop database if exists aod_system;
  2. create database if not exists aod_system;
  3. use aod_system;
  4. create table if not exists tb_video(
  5.         id int primary key auto_increment comment '视频ID',
  6.         name varchar(32) comment '视频名称',
  7.         info text comment '视频描述',
  8.         video varchar(256) comment '视频文件url,加上静态资源根目录就是实际存储路径',
  9.         image varchar(256) comment '封面图片文件url,加上静态资源根目录就是实际存储路径'
  10. );
复制代码

5.2 数据管理类的计划

数据管理模块负责同一对数据库中数据的增删改查管理,其他所有的模块要举行对数据的操纵都要通过数据管理模块来完成。
然而,数据库中大概存在多张表,每张表的数据又不雷同,举行的数据操纵也差别。因此,就需要为每张表中的数据操纵都计划一个数据管理类,通过类的实例化对象来管理这张表中的数据。
由于在视频点播体系中只涉及一张表,因此只计划一个类即可,该类包罗的数据库操纵有:新增、修改、删除、查询所有数据、查询单个数据、含糊匹配。
由于视频信息在接口之间的传递字段数目大概很多,因此使用 Json::Value 对象举行传递。以下是详细代码的实现:
  1. #ifndef __MY_DATA__ //防止头文件重复包含
  2. #define __MY_DATA__
  3. #include "util.hpp"
  4. #include <cstdlib>
  5. #include <mutex>
  6. #include <mysql/mysql.h>
  7. namespace aod
  8. {
  9. #define HSOT "127.0.0.1"
  10. #define USER "root"
  11. #define PASSWD ""
  12. #define DBNAME "aod_system"
  13.   //MYSQL句柄初始化
  14.     static MYSQL* MySQLInit()
  15.     {
  16.         MYSQL* mysql = mysql_init(NULL);
  17.         if(NULL == mysql)
  18.         {
  19.             std::cout << "init mysql instance failed!" << std::endl;
  20.             return NULL;
  21.         }
  22.         //连接数据库服务器
  23.         if(mysql_real_connect(mysql, HSOT, USER, PASSWD, DBNAME, 0, NULL, 0) == NULL)
  24.         {
  25.             std::cout << "connect mysql server failed!" << std::endl;
  26.             mysql_close(mysql);
  27.             return NULL;
  28.         }
  29.         
  30.         mysql_set_character_set(mysql, "utf8");
  31.         return mysql;
  32.     }
  33.     //释放MYSQL句柄
  34.     static void MySQLDestroy(MYSQL* mysql)
  35.     {
  36.         if(mysql != NULL)
  37.         {
  38.             mysql_close(mysql);
  39.         }
  40.         return;
  41.     }
  42.     //执行sql语句
  43.     static bool MySQLQuery(MYSQL* mysql, const std::string& sql)
  44.     {
  45.         int ret = mysql_query(mysql, sql.c_str());
  46.         if(ret != 0)
  47.         {
  48.             std::cout << sql << std::endl;
  49.             std::cout << "query sql failed!" << std::endl;
  50.             return false;
  51.         }
  52.         return true;
  53.     }
  54.     class TableVideo
  55.     {
  56.         private:
  57.             MYSQL* _mysql; //MYSQL句柄
  58.             std::mutex _mutex; //解决操作对象在多线程中操作这张表的线程安全问题
  59.         public:
  60.             //完成对mysql句柄的初始化
  61.             TableVideo()
  62.             {
  63.                 _mysql = MySQLInit();
  64.                 if(NULL == _mysql)
  65.                 {
  66.                     exit(-1);
  67.                 }
  68.             }
  69.             //释放mysql句柄
  70.             ~TableVideo()
  71.             {
  72.                 MySQLDestroy(_mysql);
  73.             }
  74.             //新增---传入视频信息
  75.             bool Insert(const Json::Value& video)
  76.             {
  77.                 //id name info video image
  78.                 std::string sql;
  79.                 sql.resize(4096 + video["info"].asString().size()); //防止视频简介内容过长
  80. #define INSERT_VIDEO "insert tb_video values(null, '%s', '%s', '%s', '%s');"
  81.                 if(video["name"].asString().size() == 0
  82.                     || video["info"].asString().size() == 0
  83.                     || video["video"].asString().size() == 0
  84.                     || video["image"].asString().size() == 0)
  85.                 {
  86.                     std::cout << "新增视频信息有误!" << std::endl;
  87.                     return false;
  88.                 }
  89.                 sprintf(&sql[0], INSERT_VIDEO, video["name"].asCString(), video["info"].asCString(), video["video"].asCString(), video["image"].asCString());
  90.                
  91.                 return MySQLQuery(_mysql, sql);
  92.             }
  93.             //修改---传入视频id和信息
  94.             bool Update(int video_id, const Json::Value& video)
  95.             {
  96.                 std::string sql;
  97.                 sql.resize(4096 + video["info"].asString().size()); //防止视频简介内容过长
  98. #define UPDATE_VIDEO "update tb_video set name='%s', info='%s' where id = %d;"
  99.                 sprintf(&sql[0], UPDATE_VIDEO, video["name"].asCString(), video["info"].asCString(), video_id);
  100.                
  101.                 return MySQLQuery(_mysql, sql);
  102.             }
  103.             //删除---传入视频id
  104.             bool Delete(int video_id)
  105.             {
  106.                 std::string sql;
  107.                 sql.resize(1024);
  108. #define DELETE_VIDEO "delete from tb_video where id=%d;"
  109.                
  110.                 sprintf(&sql[0], DELETE_VIDEO, video_id);
  111.                 return MySQLQuery(_mysql, sql);
  112.             }
  113.             //查询并输出所有视频信息
  114.             bool SelectAll(Json::Value* videos)
  115.             {
  116. #define SELECT_ALL "select * from tb_video;"
  117.                
  118.                 _mutex.lock(); // 在多线程中,保护查询与保存结果到本地的过程
  119.                 bool ret = MySQLQuery(_mysql, SELECT_ALL);
  120.                 if(false == ret)
  121.                 {
  122.                     std::cout << "select all failed!" << std::endl;
  123.                     _mutex.unlock();
  124.                     return false;
  125.                 }
  126.                
  127.                 MYSQL_RES *res = mysql_store_result(_mysql);
  128.                 if(NULL == res)
  129.                 {
  130.                     std::cout << "store result failed!" << std::endl;
  131.                     _mutex.unlock();
  132.                     return false;
  133.                 }
  134.                
  135.                 _mutex.unlock(); // 解锁
  136.                 int num_rows = mysql_num_rows(res);
  137.                 for(int i = 0; i < num_rows; ++i)
  138.                 {
  139.                     MYSQL_ROW row = mysql_fetch_row(res);
  140.                     Json::Value video;
  141.                     video["id"] =atoi(row[0]);
  142.                     video["name"] = row[1];
  143.                     video["info"] = row[2];
  144.                     video["video"] = row[3];
  145.                     video["image"] = row[4];
  146.                     
  147.                     videos->append(video);
  148.                 }
  149.                 mysql_free_result(res); //释放结果集
  150.                 return true;
  151.             }
  152.             //传入id,查询单个视频信息
  153.             bool SelectOne(int video_id, Json::Value* video)
  154.             {
  155.               
  156.               std::string sql;
  157.               sql.resize(1024);
  158. #define SELECT_ONE "select * from tb_video where id=%d;"
  159.               sprintf(&sql[0], SELECT_ONE, video_id);
  160.                
  161.                 _mutex.lock(); // 在多线程中,保护查询与保存结果到本地的过程
  162.                 bool ret = MySQLQuery(_mysql, sql);
  163.                 if(false == ret)
  164.                 {
  165.                     std::cout << "select all failed!" << std::endl;
  166.                     _mutex.unlock();
  167.                     return false;
  168.                 }
  169.                
  170.                 MYSQL_RES *res = mysql_store_result(_mysql);
  171.                 if(NULL == res)
  172.                 {
  173.                     std::cout << "store result failed!" << std::endl;
  174.                     _mutex.unlock();
  175.                     return false;
  176.                 }
  177.                
  178.                 _mutex.unlock(); // 解锁
  179.                 int num_rows = mysql_num_rows(res);
  180.                 if(num_rows != 1)
  181.                 {
  182.                     std::cout << "data is not exits!" << std::endl;
  183.                     mysql_free_result(res);
  184.                     return false;
  185.                 }
  186.               
  187.                  MYSQL_ROW row = mysql_fetch_row(res);
  188.                
  189.                  (*video)["id"] =atoi(row[0]);
  190.                  (*video)["name"] = row[1];
  191.                  (*video)["info"] = row[2];
  192.                  (*video)["video"] = row[3];
  193.                  (*video)["image"] = row[4];
  194.                 mysql_free_result(res); //释放结果集
  195.                 return true;
  196.             }
  197.             //模糊匹配---输入关键字
  198.             bool SelectLike(const std::string& key, Json::Value* videos)
  199.             {
  200.               std::string sql;
  201.               sql.resize(1024);
  202. #define SELECT_LIKE "select * from tb_video where name like '%%%s%%';"
  203.               sprintf(&sql[0], SELECT_LIKE, key.c_str());
  204.                
  205.                 _mutex.lock(); // 在多线程中,保护查询与保存结果到本地的过程
  206.                 bool ret = MySQLQuery(_mysql, SELECT_ALL);
  207.                 if(false == ret)
  208.                 {
  209.                     std::cout << "select all failed!" << std::endl;
  210.                     _mutex.unlock();
  211.                     return false;
  212.                 }
  213.                
  214.                 MYSQL_RES *res = mysql_store_result(_mysql);
  215.                 if(NULL == res)
  216.                 {
  217.                     std::cout << "store result failed!" << std::endl;
  218.                     _mutex.unlock();
  219.                     return false;
  220.                 }
  221.                
  222.                 _mutex.unlock(); // 解锁
  223.                 int num_rows = mysql_num_rows(res);
  224.                 for(int i = 0; i < num_rows; ++i)
  225.                 {
  226.                     MYSQL_ROW row = mysql_fetch_row(res);
  227.                     Json::Value video;
  228.                     video["id"] =atoi(row[0]);
  229.                     video["name"] = row[1];
  230.                     video["info"] = row[2];
  231.                     video["video"] = row[3];
  232.                     video["image"] = row[4];
  233.                     
  234.                     videos->append(video);
  235.                 }
  236.                 mysql_free_result(res); //释放结果集
  237.                 return true;
  238.             }
  239.     };
  240. }
  241. #endif
复制代码
六、网络通信模块 — 网络通信接口计划

首先要明白的是:


  • 网络通信接口计划其实就是界说好:什么样的请求是一个查询请求、什么样的请求是一个删除请求 
  • 服务端进步的功能包括:新增视频、删除视频、修改视频、查询所有视频、查询单个视频、含糊匹配查询。
因此,要让差别的功能对应到差别的接口,在网络通信接口的计划中,就借助了 REST计划风格来计划网络接口。
6.1 REST 计划风格

REST 是 Representational State Transfer的缩写,中文名叫体现层状态转换。是Roy Thomas Fielding博士于2000年在他的博士论文中提出来的一种万维网软件架构风格,目的是便于差别软件或步调在网络(比方互联网)中相互传递信息。
REST是基于HTTP协议之上而确定的一组束缚和属性,可以充分利用HTTP协议的各种功能,是HTTP协议的最佳实践。RESTful API是一种软件架构风格,可以让软件更加的清楚、简介、富有层次感、进步可维护性。
匹配于 REST这种架构风格的网络服务,允许客户端发出以URL访问和操纵网络资源的请求,而与预先界说好的无状态操纵集同等化。因此体现层状态转换提供了在互联网络的计算体系之间,相互资源可交互使用的协作性质。
在REST风格中界说了:


  • GET方法:表示查询
  • POST方法:表示新增
  • PUT方法:表示修改
  • DELETE方法:表示删除
  • 资源正文数据接纳Json、XML数据格式
6.2 REST 风格下 CRUD 操纵的 HTTP 格式

获取所有视频信息:
  1. 请求:
  2. GET /video HTTP/1.1
  3. Connection: keep-alive
  4. ......
  5. 响应:
  6. HTTP/1.1 200 OK
  7. Content-Length: xxx
  8. Content-Type: application/json
  9. ......
  10. [
  11.         {
  12.                 "id": 1,
  13.                 "name": "电影1",
  14.                 "info": "xxxxxx",
  15.                 "video": "/video/movie1.mp4",
  16.                 "image": "/img/thumbs/movie1.png",
  17.         },
  18.         {
  19.                 "id": 2,
  20.                 "name": "电影2",
  21.                 "info": "xxxxxx",
  22.                 "video": "/video/movie2.mp4",
  23.                 "image": "/img/thumbs/movie2.png",
  24.         }
  25. ]
复制代码
搜索关键字获取视频信息:
  1. 请求:
  2. GET /video?search="电影1" HTTP/1.1
  3. Connection: keep-alive
  4. ......
  5. 响应:
  6. HTTP/1.1 200 OK
  7. Content-Length: xxx
  8. Content-Type: application/json
  9. ......
  10. [
  11.         {
  12.                 "id": 1,
  13.                 "name": "电影1",
  14.                 "info": "xxxxxx",
  15.                 "video": "/video/movie1.mp4",
  16.                 "image": "/img/thumbs/movie1.png",
  17.         }
  18. ]
复制代码
获取指定视频信息:
  1. 请求:
  2. GET /video/1 HTTP/1.1
  3. Connection: keep-alive
  4. ......
  5. 响应:
  6. HTTP/1.1 200 OK
  7. Content-Length: xxx
  8. Content-Type: application/json
  9. ......
  10. [
  11.         {
  12.                 "id": 1,
  13.                 "name": "电影1",
  14.                 "info": "xxxxxx",
  15.                 "video": "/video/movie1.mp4",
  16.                 "image": "/img/thumbs/movie1.png",
  17.         }
  18. ]
复制代码
删除指定视频信息:
  1. 请求:
  2. DELETE /video/1 HTTP/1.1
  3. Connection: keep-alive
  4. ......
  5. 响应:
  6. HTTP/1.1 200 OK
  7. ......
复制代码
修改指定视频信息:
  1. 请求:
  2. PUT /video/1 HTTP/1.1
  3. Connection: keep-alive
  4. ......
  5. 响应:
  6. HTTP/1.1 200 OK
  7. ......
复制代码
上传视频信息:
因为在上传视频信息的时候,会携带有视频文件、封面图片文件的上传,而这些文件数据都是二进制的,所以使用Json格式就不再符合了。因此在上传视频的时候就使用HTTP协议默认的上传文件请求格式,而不使用REST风格。
  1. 请求:
  2. PSOT /video HTTP/1.1
  3. Content-Type: video/form-data; boundary="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
  4. Content-Length: xxx
  5. ......
  6. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  7. Content-Disposition: form-data; name="name"
  8. name(视频的名称)
  9. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  10. Content-Disposition: form-data; name="info"
  11. info(视频的描述)
  12. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  13. Content-Disposition: form-data; name="video"; filename="video.mp4"
  14. Content-Type: text/plain
  15. video视频数据
  16. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  17. Content-Disposition: form-data; name="image"; filename="image.jpg"
  18. Content-Type: text/plain
  19. image封面图片数据
  20. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  21. Content-Disposition: form-data; name="submit"
  22. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  23. 响应:
  24. HTTP/1.1 303 See Other
  25. Location: "/"
复制代码
七、业务处理模块的实现

7.1 业务处理模块类的计划

业务处理模块负责与客户端举行网络通信,接收客户端的请求,然后根据请求信息,明白客户端用户的意图举行业务处理,并返回相应的处理结果给客户端。
由于在实现网络通信相关功能使用的是httplib库,大大减小了开辟成本,因此这里将网络通信模块和业务处理模块合并在同一个类中。因此在视频点播体系中,业务处理模块重要包罗两大功能:网络通信功能和业务处理功能
业务处理模块重要完成的功能有:


  • 客户端的视频数据和信息的上传
  • 客户端的视频列表的展示
  • 客户端的观看视频请求
  • 客户端的视频管理(修改、删除)
代码框架如下:
  1. #ifndef __MY_SERVER__
  2. #define __MY_SERVER__
  3. #include "data.hpp"
  4. #include "httplib.h"
  5. namespace aod
  6. {
  7. #define WWW_ROOT "./www"      //资源根目录
  8. #define VIDEO_ROOT "/video/"  //视频目录
  9. #define IMAGE_ROOT "/image/"  //图片目录
  10.     // 因为 httplib 是基于多线程,因此数据管理模块需要在多线程被访问,为了便于访问定义全局变量
  11.     aod::TableVideo * table_video = NULL;
  12.     class Server
  13.     {
  14.         private:
  15.             int _port; //服务器监听的端口号
  16.             httplib::Server _server; //用于搭建HTTP服务器
  17.         public:
  18.             //业务处理接口
  19.             
  20.             //新增
  21.             static void Insert(const httplib::Request& req, httplib::Response& rsp);
  22.             
  23.             //修改
  24.             static void Update(const httplib::Request& req, httplib::Response& rsp);
  25.             
  26.             //删除
  27.             static void Delete(const httplib::Request& req, httplib::Response& rsp);
  28.             
  29.             //查询单个
  30.             static void SelectOne(const httplib::Request& req, httplib::Response& rsp);
  31.             
  32.             //查询所有或者模糊匹配
  33.             static void SelectAll(const httplib::Request& req, httplib::Response& rsp);
  34.         public:
  35.             Server(int port) :_port(port){}
  36.             
  37.             //建立请求与处理函数之间的映射关系,设置静态资源根目录,启动服务器
  38.             bool RunModule();
  39.     };
  40. }
  41. #endif
复制代码
RunModule的实现:
  1. bool RunModule()
  2. {
  3.     //1. 初始化---初始化数据管理模块、创建指定的目录
  4.     table_video = new TableVideo();
  5.     if (aod::FileUtil(WWW_ROOT).CreateDirectory() == false)
  6.     {
  7.         std::cout << "create directory: " << WWW_ROOT << " failed!" << std::endl;
  8.         return false;
  9.     }
  10.     std::string video_real_path = std::string(WWW_ROOT)+ std::string(VIDEO_ROOT);  // ./www/video/
  11.     std::string image_real_path = std::string(WWW_ROOT)+ std::string(IMAGE_ROOT);  // ./www/image/
  12.     if (aod::FileUtil(video_real_path).CreateDirectory() == false)
  13.     {
  14.         std::cout << "create directory: " << video_real_path << " failed!" << std::endl;
  15.         return false;
  16.     }
  17.     if (aod::FileUtil(image_real_path).CreateDirectory() == false)
  18.     {
  19.         std::cout << "create directory: " << image_real_path << " failed!" << std::endl;
  20.         return false;
  21.     }
  22.     // 2. 搭建HTTP服务器,开始运行
  23.     // 2.1 设置静态资源根目录
  24.     _server.set_mount_point("/", WWW_ROOT);
  25.     // 2.2 建立请求与处理函数之间的映射关系
  26.     _server.Post("/video", Insert);
  27.     _server.Delete("/video/(\\d+)", Delete);
  28.     _server.Put("/video/(\\d+)", Update);
  29.     _server.Get("/video/(\\d+)", SelectOne);
  30.     _server.Get("/video", SelectAll);
  31.    
  32.     // 3. 启动服务器
  33.     _server.listen("0.0.0.0", _port);
  34.    
  35.     return true;
  36. }
复制代码
Insert的实现:
  1. static void Insert(const httplib::Request& req, httplib::Response& rsp)
  2. {   
  3.     if(req.has_file("name") == false  ||
  4.        req.has_file("info") == false  ||
  5.        req.has_file("video") == false ||
  6.        req.has_file("image") == false )
  7.     {
  8.         rsp.status = 400;  //客户端错误,请求包含语法错误或无法完成请求
  9.         rsp.body = R"({"result":false, "reason":"上传的数据信息错误"})";
  10.         rsp.set_header("Content-Type", "application/json");
  11.         return;
  12.     }
  13.     //视频名称
  14.     httplib::MultipartFormData name = req.get_file_value("name");
  15.     //视频描述
  16.     httplib::MultipartFormData info = req.get_file_value("info");
  17.     //视频文件
  18.     httplib::MultipartFormData video = req.get_file_value("video");
  19.     //图片文件
  20.     httplib::MultipartFormData image = req.get_file_value("image");
  21.     //保存视频和图片文件到磁盘
  22.         //MultipartFormData {name, content_type, filename, content}
  23.     std::string video_name = name.content; //视频名称内容
  24.         std::string video_info = info.content; //视频描述内容
  25.    
  26.         //视频和图片文件存储路径 例如:./www/video/视频1.mp4
  27.    
  28.     std::string root = WWW_ROOT;
  29.         std::string video_path = root + VIDEO_ROOT + video_name + video.filename;
  30.         std::string image_path = root + IMAGE_ROOT + video_name + image.filename;
  31.         if (aod::FileUtil(video_path).SetContent(video.content) == false)
  32.     {
  33.         rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  34.         rsp.body = R"({"result":false, "reason":"视频文件存储失败"})";
  35.         rsp.set_header("Content-Type", "application/json");
  36.         return;
  37.     }
  38.     if (aod::FileUtil(image_path).SetContent(image.content) == false)
  39.     {
  40.         rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  41.         rsp.body = R"({"result":false, "reason":"图片文件存储失败"})";
  42.         rsp.set_header("Content-Type", "application/json");
  43.         return;
  44.     }
  45.    
  46.         //数据库新增数据
  47.         Json::Value video_json;
  48.         video_json["name"] = video_name;
  49.         video_json["info"] = video_info;
  50.         video_json["video"] = VIDEO_ROOT + video_name + video.filename; // /video/视频1video.mp4
  51.         video_json["image"] = IMAGE_ROOT + video_name + image.filename; // /image/视频1image.jpg
  52.        
  53.         if (table_video->Insert(video_json) == false)
  54.         {
  55.             rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  56.             rsp.body = R"({"result":false, "reason":"数据库新增数据失败"})";
  57.             rsp.set_header("Content-Type", "application/json");
  58.             return;
  59.         }
  60.        
  61.         return;
  62. }
复制代码
Update的实现:
  1. static void Update(const httplib::Request& req, httplib::Response& rsp)
  2. {
  3.      // 1. 获取要修改的视频id和修改后的视频信息
  4.      std::string num = req.matches[1];
  5.      int video_id = atoi(num.c_str());
  6.      Json::Value video;
  7.      if(aod::JsonUtil::Deserialize(req.body, &video) == false)
  8.      {
  9.          rsp.status = 400;  //客户端错误,请求包含语法错误或无法完成请求
  10.          rsp.body = R"({"result":false, "reason":"新的视频信息解析失败"})";
  11.          rsp.set_header("Content-Type", "application/json");
  12.          return;
  13.      }
  14.      // 2. 修改数据库视频信息
  15.      
  16.      if(table_video->Update(video_id, video) == false)
  17.      {
  18.          rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  19.          rsp.body = R"({"result":false, "reason":"修改数据库中的视频信息失败"})";
  20.          rsp.set_header("Content-Type", "application/json");
  21.          return;
  22.      }
  23.      return;
  24. }
复制代码
Delete的实现:
  1. static void Delete(const httplib::Request& req, httplib::Response& rsp)
  2. {
  3.     //1. 获取要删除的视频id
  4.    
  5.     //matches: 存放正则表达式匹配的规则数据 /numbers/123 matches[0] = "/numbers/123", matches[1] = "123"
  6.     std::string num = req.matches[1];
  7.     int video_id = atoi(num.c_str());  
  8.     //2. 删除视频文件和图片文件
  9.     Json::Value video;
  10.     if(table_video->SelectOne(video_id, &video) == false)
  11.     {
  12.         rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  13.         rsp.body = R"({"result":false, "reason":"数据库中不存在视频信息"})";
  14.         rsp.set_header("Content-Type", "application/json");
  15.         return;
  16.     }
  17.     std::string root = WWW_ROOT;
  18.     //视频文件存放路径
  19.     std::string video_path = root + video["video"].asString();
  20.     //封面图片存放路径
  21.     std::string image_path = root + video["image"].asString();
  22.     remove(video_path.c_str());
  23.     remove(image_path.c_str());
  24.    
  25.    
  26.     //3. 删除数据库信息
  27.     if(table_video->Delete(video_id) == false)
  28.     {
  29.         rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  30.         rsp.body = R"({"result":false, "reason":"删除数据库视频信息失败"})";
  31.         rsp.set_header("Content-Type", "application/json");
  32.         return;
  33.     }
  34.    
  35.     return;
  36. }
复制代码
SelectOne的实现:
  1. static void SelectOne(const httplib::Request& req, httplib::Response& rsp)
  2. {
  3.     //1. 获取要删除的视频id
  4.     std::string num = req.matches[1];
  5.     int video_id = atoi(num.c_str());  
  6.     //2. 在数据库中查询指定视频信息
  7.     Json::Value video;
  8.     if(table_video->SelectOne(video_id, &video) == false)
  9.     {
  10.         rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  11.         rsp.body = R"({"result":false, "reason":"查询数据库指定视频信息失败"})";
  12.         rsp.set_header("Content-Type", "application/json");
  13.         return;
  14.     }
  15.     //3. 组织响应正文 --- json格式的字符串
  16.     if (aod::JsonUtil::Serialize(video, &rsp.body) == false)
  17.     {
  18.         rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  19.         rsp.body = R"({"result":false, "reason":"序列化正文失败"})";
  20.         rsp.set_header("Content-Type", "application/json");
  21.         return;
  22.     }
  23.     rsp.set_header("Content-Type", "application/json");
  24.     return;
  25. }
复制代码
SelectAll的实现:
  1. static void SelectAll(const httplib::Request& req, httplib::Response& rsp)
  2. {
  3.     //存在两种可能: /video  和  /video?search="关键字"
  4.    
  5.     // 1. 判断查询类型
  6.   
  7.     bool select_flag = true; //默认查询所有
  8.     std::string search_key;
  9.     if (req.has_param("search") == true)
  10.     {
  11.         select_flag = false; //模糊匹配
  12.         search_key = req.get_param_value("search");
  13.     }
  14.    
  15.     //2. 查询视频信息
  16.     Json::Value videos;
  17.     if(select_flag == true)
  18.     {
  19.         if (table_video->SelectAll(&videos) == false)
  20.         {
  21.             rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  22.             rsp.body = R"({"result":false, "reason":"查询数据库所有视频信息失败"})";
  23.             rsp.set_header("Content-Type", "application/json");
  24.             return;
  25.         }
  26.     }
  27.     else
  28.     {
  29.         if (table_video->SelectLike(search_key, &videos) == false)
  30.         {
  31.             rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  32.             rsp.body = R"({"result":false, "reason":"模糊匹配查询数据库视频信息失败"})";
  33.             rsp.set_header("Content-Type", "application/json");
  34.             return;
  35.         }
  36.     }
  37.    
  38.     //3. 组织响应正文
  39.     if (aod::JsonUtil::Serialize(videos, &rsp.body) == false)
  40.     {
  41.         rsp.status = 500;  //服务器错误,服务器在处理请求的过程中发生了错误
  42.         rsp.body = R"({"result":false, "reason":"序列化正文失败"})";
  43.         rsp.set_header("Content-Type", "application/json");
  44.         return;
  45.     }
  46.    
  47.     rsp.set_header("Content-Type", "application/json");
  48.     return;
  49. }
复制代码
【注意】
在SelectAll函数中将查询所有视频和含糊匹配两个功能包罗在一起的,因为在httplib库中的Resuest类中有一个has_param函数,可用于判定请求中是否寄义search关键字。利用has_param函数就可判定出此次查询请求是查询所有还是通过关键字查询。
7.2 综合调试

调试代码:
  1. #include "server.hpp"
  2. int main()
  3. {
  4.         aod::Server server(9090);
  5.         server.RunModule();
  6.         return 0;
  7. }
复制代码
服务器的功能测试借助一个工具 Postman 完成。Postman下载地址 
 
八、前端界面的实现

8.1 前端视频展示界面的实现

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.         <meta charset="utf-8">
  5.         <meta name="viewport" content="width=device-width, initial-scale=1">
  6.         <meta name="description" content="">
  7.         <meta name="author" content="OrcasThemes">
  8.         <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  9.         <title>Home</title>
  10.         <!-- Bootstrap core CSS -->
  11.         <link href="css/bootstrap.css" rel="stylesheet">
  12.         <!-- Custom styles for this template -->
  13.         <link rel="stylesheet" href="css/screen.css">
  14.         <link rel="stylesheet" href="css/animation.css">
  15.         <!--[if IE 7]>
  16. <![endif]-->
  17.         <link rel="stylesheet" href="css/font-awesome.css">
  18.         <!--[if lt IE 8]>
  19. <link rel="stylesheet" href="css/ie.css" type="text/css" media="screen, projection">
  20. <![endif]-->
  21.         <link href="css/lity.css" rel="stylesheet">
  22.         <style>
  23.                 [v-cloak] {
  24.                         display: none;
  25.                 }
  26.         </style>
  27. </head>
  28. <body>
  29.         <div id="myapp">
  30.                 <!-- HOME 1 -->
  31.                 <div id="home1" class="container-fluid standard-bg">
  32.                         <!-- HEADER -->
  33.                         <div class="row header-top">
  34.                                 <div class="col-lg-3 col-md-6 col-sm-5 col-xs-8">
  35.                                         <a class="main-logo" href="#"><img src="img/main-logo.png" class="main-logo img-responsive"
  36.                                                         alt="Muvee Reviews" title="Muvee Reviews"></a>
  37.                                 </div>
  38.                                 <div class="col-lg-6 hidden-md text-center hidden-sm hidden-xs">
  39.                                 </div>
  40.                                 <div class="col-lg-3 col-md-6 col-sm-7 hidden-xs">
  41.                                         <div class="right-box">
  42.                                                 <button type="button" class="access-btn" data-toggle="modal" data-target="#enquirypopup">新增视频</button>
  43.                                         </div>
  44.                                 </div>
  45.                         </div>
  46.                         <!-- MENU -->
  47.                         <div class="row home-mega-menu ">
  48.                                 <div class="col-md-12">
  49.                                         <nav class="navbar navbar-default">
  50.                                                 <div class="navbar-header">
  51.                                                         <button class="navbar-toggle" type="button" data-toggle="collapse"
  52.                                                                 data-target=".js-navbar-collapse">
  53.                                                                 <span class="sr-only">Toggle navigation</span>
  54.                                                                 <span class="icon-bar"></span>
  55.                                                                 <span class="icon-bar"></span>
  56.                                                                 <span class="icon-bar"></span>
  57.                                                         </button>
  58.                                                 </div>
  59.                                                 <div class="collapse navbar-collapse js-navbar-collapse megabg dropshd ">
  60.                                                         <ul class="nav navbar-nav">
  61.                                                                 <li><a href="index.html">视频点播</a></li>
  62.                                                         </ul>
  63.                                                         <div class="search-block">
  64.                                                                 <form>
  65.                                                                         <input type="search" placeholder="Search">
  66.                                                                 </form>
  67.                                                         </div>
  68.                                                 </div>
  69.                                                 <!-- /.nav-collapse -->
  70.                                         </nav>
  71.                                 </div>
  72.                         </div>
  73.                         <!-- CORE -->
  74.                         <div class="row">
  75.                                 <!-- SIDEBAR -->
  76.                                 <div class="col-lg-2 col-md-4 hidden-sm hidden-xs">
  77.                                 </div>
  78.                                 <!-- HOME MAIN POSTS -->
  79.                                 <div class="col-lg-10 col-md-8">
  80.                                         <section id="home-main">
  81.                                                 <h2 class="icon"><i class="fa fa-television" aria-hidden="true"></i>视频列表</h2>
  82.                                                 <div class="row">
  83.                                                         <!-- ARTICLES -->
  84.                                                         <div class="col-lg-9 col-md-12 col-sm-12">
  85.                                                                 <div class="row auto-clear">
  86.                                                                         <article class="col-lg-3 col-md-6 col-sm-4" v-for="video in videos">
  87.                                                                                 <!-- POST L size -->
  88.                                                                                 <div class="post post-medium">
  89.                                                                                         <div class="thumbr">
  90.                                                                                                 <a class="afterglow post-thumb" v-bind:href="'/video.html?id='+video.id" target="_blank">
  91.                                                                                                         <span class="play-btn-border" title="Play"><i
  92.                                                                                                                         class="fa fa-play-circle headline-round"
  93.                                                                                                                         aria-hidden="true"></i></span>
  94.                                                                                                         <div class="cactus-note ct-time font-size-1"><span></span>
  95.                                                                                                         </div>
  96.                                                                                                         <img class="img-responsive" v-bind:src="video.image" alt="#"
  97.                                                                                                                 v-cloak>
  98.                                                                                                 </a>
  99.                                                                                         </div>
  100.                                                                                         <div class="infor">
  101.                                                                                                 <h4>
  102.                                                                                                         <a class="title" href="#" v-cloak>{{video.name}}</video></a>
  103.                                                                                                 </h4>
  104.                                                                                                 <!-- <span class="posts-txt" title="Posts from Channel"><i
  105.                                                                                                                 class="fa fa-thumbs-up" aria-hidden="true"></i>20.895</span>
  106.                                                                                                 <div class="ratings">
  107.                                                                                                         <i class="fa fa-star" aria-hidden="true"></i>
  108.                                                                                                         <i class="fa fa-star" aria-hidden="true"></i>
  109.                                                                                                         <i class="fa fa-star-half-o" aria-hidden="true"></i>
  110.                                                                                                         <i class="fa fa-star-o"></i>
  111.                                                                                                         <i class="fa fa-star-half"></i>
  112.                                                                                                 </div> -->
  113.                                                                                         </div>
  114.                                                                                 </div>
  115.                                                                         </article>
  116.                                                                 </div>
  117.                                                                 <div class="clearfix spacer"></div>
  118.                                                         </div>
  119.                                                         <!-- RIGHT ASIDE -->
  120.                                                         <div class="col-lg-3 hidden-md col-sm-12 text-center top-sidebar">
  121.                                                         </div>
  122.                                                 </div>
  123.                                         </section>
  124.                                 </div>
  125.                         </div>
  126.                 </div>
  127.                 <!-- CHANNELS -->
  128.                 <div id="channels-block" class="container-fluid channels-bg">
  129.                 </div>
  130.                 <!-- BOTTOM BANNER -->
  131.                 <div id="bottom-banner" class="container text-center">
  132.                 </div>
  133.                 <!-- FOOTER -->
  134.                 <div id="footer" class="container-fluid footer-background">
  135.                         <div class="container">
  136.                                 <footer>
  137.                                         <!-- SECTION FOOTER -->
  138.                                         <div class="row">
  139.                                                 <!-- SOCIAL -->
  140.                                                 <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
  141.                                                         <div class="row auto-clear">
  142.                                                         </div>
  143.                                                 </div>
  144.                                                 <!-- TAGS -->
  145.                                                 <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
  146.                                                 </div>
  147.                                                 <!-- POST -->
  148.                                                 <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
  149.                                                 </div>
  150.                                                 <!-- LINKS -->
  151.                                                 <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
  152.                                                 </div>
  153.                                         </div>
  154.                                         <div class="row copyright-bottom text-center">
  155.                                                 <div class="col-md-12 text-center">
  156.                                                         <a href="" class="footer-logo" title="Video Magazine Bootstrap HTML5 template">
  157.                                                                 <img src="img/footer-logo.png" class="img-responsive text-center"
  158.                                                                         alt="Video Magazine Bootstrap HTML5 template">
  159.                                                         </a>
  160.                                                         <p v-cloak>Copyright &copy; Author by {{author}}</p>
  161.                                                 </div>
  162.                                         </div>
  163.                                 </footer>
  164.                         </div>
  165.                 </div>
  166.                 <!-- MODAL -->
  167.                 <div id="enquirypopup" class="modal fade in " role="dialog">
  168.                         <div class="modal-dialog">
  169.                                 <!-- Modal content-->
  170.                                 <div class="modal-content row">
  171.                                         <div class="modal-header custom-modal-header">
  172.                                                 <button type="button" class="close" data-dismiss="modal">×</button>
  173.                                                 <h2 class="icon"><i class="fa fa-television" aria-hidden="true"></i>新增视频</h2>
  174.                                         </div>
  175.                                         <div class="modal-body">
  176.                                                 <form name="info_form" class="form-inline" action="/video" method="post" enctype="multipart/form-data">
  177.                                                        
  178.                                                         <div class="form-group col-sm-12">
  179.                                                                 <input type="text" class="form-control" name="name" placeholder="输入视频名称">
  180.                                                         </div>
  181.                                                        
  182.                                                         <div class="form-group col-sm-12">
  183.                                                                 <input type="text" class="form-control" name="info" placeholder="输入视频简介">
  184.                                                         </div>
  185.                                                         <div class="form-group col-sm-12">
  186.                                                                 <input type="file" class="form-control" name="video" placeholder="选择视频文件">
  187.                                                         </div>
  188.                                                        
  189.                                                         <div class="form-group col-sm-12">
  190.                                                                 <input type="file" class="form-control" name="image" placeholder="选择封面图片">
  191.                                                         </div>
  192.                                                         <div class="form-group col-sm-12">
  193.                                                                 <button class="subscribe-btn pull-right" type="submit"title="Subscribe">上传</button>
  194.                                                         </div>
  195.                                                 </form>
  196.                                         </div>
  197.                                 </div>
  198.                         </div>
  199.                 </div>
  200.         </div>
  201. </body>
  202. <!-- JAVA SCRIPT -->
  203. <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
  204. <script src="js/jquery-1.12.1.min.js"></script>
  205. <script src="js/bootstrap.min.js"></script>
  206. <script src="js/lity.js"></script>
  207. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  208. <script>
  209.         $(".nav .dropdown").hover(function () {
  210.                 $(this).find(".dropdown-toggle").dropdown("toggle");
  211.         });
  212. </script>
  213. <script>
  214.         let app = new Vue({
  215.                 el: '#myapp',
  216.                 data: {
  217.                         author: "Lihaifei",
  218.                         videos: []
  219.                 },
  220.                 methods: {
  221.                         get_allvideos: function () {
  222.                                 $.ajax({
  223.                                         url: "/video",
  224.                                         type: "get",
  225.                                         context: this, // 将vue传入ajax作为this对象
  226.                                         success: function (result, status, xhr) { //请求成功后的处理函数
  227.                                                 this.videos = result;
  228.                                                 // alert("获取结果成功!");
  229.                                         }
  230.                                 })
  231.                         }
  232.                 }
  233.         });
  234.         app.get_allvideos();
  235. </script>
  236. </html>
复制代码
8.2 前端视频观看页面的实现 

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.    <meta charset="utf-8">
  5.    <meta name="viewport" content="width=device-width, initial-scale=1">
  6.    <meta name="description" content="">
  7.    <meta name="author" content="OrcasThemes">
  8.    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  9.    <title></title>
  10.    <!-- Bootstrap core CSS -->
  11.    <link href="css/bootstrap.css" rel="stylesheet">
  12.    <!-- Custom styles for this template -->
  13.    <link rel="stylesheet" href="css/screen.css">
  14.    <link rel="stylesheet" href="css/animation.css">
  15.    <!--[if IE 7]>
  16.       
  17.       <![endif]-->
  18.    <link rel="stylesheet" href="css/font-awesome.css">
  19.    <!--[if lt IE 8]>
  20.       <link rel="stylesheet" href="css/ie.css" type="text/css" media="screen, projection">
  21.       <![endif]-->
  22.    <link href="css/lity.css" rel="stylesheet">
  23.    <style>
  24.       [v-cloak] {
  25.          display: none;
  26.       }
  27.    </style>
  28. </head>
  29. <body>
  30.    <div id="myapp">
  31.       <!-- SINGLE VIDEO -->
  32.       <div id="single-video" class="container-fluid standard-bg">
  33.          <!-- HEADER -->
  34.          <div class="row header-top">
  35.             <div class="col-lg-3 col-md-6 col-sm-5">
  36.                <a class="main-logo" href="#"><img src="img/main-logo.png" class="main-logo" alt="Muvee Reviews"
  37.                      title="Muvee Reviews"></a>
  38.             </div>
  39.             <div class="col-lg-6 hidden-md text-center hidden-sm hidden-xs">
  40.             </div>
  41.             <div class="col-lg-3 col-md-6 col-sm-7 hidden-xs">
  42.                <div class="right-box">
  43.                   <button type="button" class="access-btn" data-toggle="modal" v-on:click="delete_video()">视频删除</button>
  44.                   <button type="button" class="access-btn" data-toggle="modal" data-target="#enquirypopup">视频修改</button>
  45.                </div>
  46.             </div>
  47.          </div>
  48.          <!-- MENU -->
  49.          <div class="row home-mega-menu ">
  50.             <div class="col-md-12">
  51.                <nav class="navbar navbar-default">
  52.                   <div class="navbar-header">
  53.                      <button class="navbar-toggle" type="button" data-toggle="collapse"
  54.                         data-target=".js-navbar-collapse">
  55.                         <span class="sr-only">Toggle navigation</span>
  56.                         <span class="icon-bar"></span>
  57.                         <span class="icon-bar"></span>
  58.                         <span class="icon-bar"></span>
  59.                      </button>
  60.                   </div>
  61.                   <div class="collapse navbar-collapse js-navbar-collapse megabg dropshd ">
  62.                      <ul class="nav navbar-nav">
  63.                         <li><a href="index.html">视频点播</a></li>
  64.                      </ul>
  65.                      <div class="search-block">
  66.                         <form>
  67.                            <input type="search" placeholder="Search">
  68.                         </form>
  69.                      </div>
  70.                   </div>
  71.                   <!-- /.nav-collapse -->
  72.                </nav>
  73.             </div>
  74.          </div>
  75.          <!-- SINGLE VIDEO -->
  76.          <div class="row">
  77.             <!-- SIDEBAR -->
  78.             <div class="col-lg-2 col-md-4 hidden-sm hidden-xs">
  79.             </div>
  80.             <!-- SINGLE VIDEO -->
  81.             <div id="single-video-wrapper" class="col-lg-10 col-md-8">
  82.                <div class="row">
  83.                   <!-- VIDEO SINGLE POST -->
  84.                   <div class="col-lg-9 col-md-12 col-sm-12">
  85.                      <!-- POST L size -->
  86.                      <article class="post-video">
  87.                         <!-- VIDEO INFO -->
  88.                         <div class="video-info">
  89.                            <!-- 16:9 aspect ratio -->
  90.                            <div class="embed-responsive embed-responsive-16by9 video-embed-box">
  91.                               <iframe v-bind:src="video.video" class="embed-responsive-item"></iframe>
  92.                            </div>
  93.                            <!-- <div class="metabox">
  94.                               <span class="meta-i">
  95.                                  <i class="fa fa-thumbs-up" aria-hidden="true"></i>20.895
  96.                               </span>
  97.                               <span class="meta-i">
  98.                                  <i class="fa fa-thumbs-down" aria-hidden="true"></i>3.981
  99.                               </span>
  100.                               <span class="meta-i">
  101.                                  <i class="fa fa-user"></i><a href="#" class="author" title="John Doe">John Doe</a>
  102.                               </span>
  103.                               <span class="meta-i">
  104.                                  <i class="fa fa-clock-o"></i>March 16. 2017
  105.                               </span>
  106.                               <span class="meta-i">
  107.                                  <i class="fa fa-eye"></i>1,347,912 views
  108.                               </span>
  109.                               <div class="ratings">
  110.                                  <i class="fa fa-star" aria-hidden="true"></i>
  111.                                  <i class="fa fa-star" aria-hidden="true"></i>
  112.                                  <i class="fa fa-star-half-o" aria-hidden="true"></i>
  113.                                  <i class="fa fa-star-o"></i>
  114.                                  <i class="fa fa-star-half"></i>
  115.                               </div>
  116.                            </div> -->
  117.                         </div>
  118.                         <div class="clearfix spacer"></div>
  119.                         <!-- DETAILS -->
  120.                         <div class="video-content">
  121.                            <h2 class="title main-head-title">视频描述</h2>
  122.                            <p v-cloak>{{video.info}}</p>
  123.                         </div>
  124.                         <div class="clearfix spacer"></div>
  125.                      </article>
  126.                   </div>
  127.                   <!-- VIDEO SIDE BANNERS -->
  128.                   <div class="col-lg-3 hidden-md hidden-sm">
  129.                   </div>
  130.                </div>
  131.                <div class="clearfix spacer"></div>
  132.                <div class="row">
  133.                </div>
  134.             </div>
  135.          </div>
  136.       </div>
  137.       <!-- CHANNELS -->
  138.       <div id="channels-block" class="container-fluid channels-bg">
  139.          <div class="container-fluid ">
  140.             <div class="col-md-12">
  141.                <div class="clearfix"></div>
  142.             </div>
  143.          </div>
  144.       </div>
  145.       <!-- FOOTER -->
  146.       <div id="footer" class="container-fluid footer-background">
  147.          <div class="container">
  148.             <footer>
  149.                <div class="row copyright-bottom text-center">
  150.                   <div class="col-md-12 text-center">
  151.                      <a href="" class="footer-logo" title="Video Magazine Bootstrap HTML5 template">
  152.                         <img src="img/footer-logo.png" class="img-responsive text-center"
  153.                            alt="Video Magazine Bootstrap HTML5 template">
  154.                      </a>
  155.                      <p v-cloak>Copyright &copy; Author by {{author}}</p>
  156.                   </div>
  157.                </div>
  158.             </footer>
  159.          </div>
  160.       </div>
  161.       <!-- MODAL -->
  162.       <div id="enquirypopup" class="modal fade in " role="dialog">
  163.          <div class="modal-dialog">
  164.             <!-- Modal content-->
  165.             <div class="modal-content row">
  166.                <div class="modal-header custom-modal-header">
  167.                   <button type="button" class="close" data-dismiss="modal">×</button>
  168.                   <h2 class="icon"><i class="fa fa-television" aria-hidden="true"></i>视频信息修改</h2>
  169.                </div>
  170.                <div class="modal-body">
  171.                   <form name="info_form" class="form-inline" action="#" method="post">
  172.                      <div class="form-group col-sm-12">
  173.                         <input type="text" class="form-control" name="name" v-model="video.name">
  174.                      </div>
  175.                      <div class="form-group col-sm-12">
  176.                         <input type="text" class="form-control" name="info" v-model="video.info">
  177.                      </div>
  178.                      <div class="form-group col-sm-12">
  179.                         <button class="subscribe-btn pull-right" type="submit" title="Subscribe"
  180.                            v-on:click.prevent="update_video()">提交</button>
  181.                      </div>
  182.                   </form>
  183.                </div>
  184.             </div>
  185.          </div>
  186.       </div>
  187.    </div>
  188. </body>
  189. <!-- JAVA SCRIPT -->
  190. <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
  191. <script src="js/jquery-1.12.1.min.js"></script>
  192. <script src="js/bootstrap.min.js"></script>
  193. <script src="js/lity.js"></script>
  194. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  195. <script>
  196.    $(".nav .dropdown").hover(function () {
  197.       $(this).find(".dropdown-toggle").dropdown("toggle");
  198.    });
  199. </script>
  200. <script>
  201.    let app = new Vue({
  202.       el: '#myapp',
  203.       data: {
  204.          author: "Lihaifei",
  205.          video: {}
  206.       },
  207.       methods: {
  208.          get_param: function (name) {
  209.             return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)\
  210. (&|#|;|$)').exec(location.href) || [, ""])[1].replace(/\+/g, '%20')) || null
  211.          },
  212.          get_video: function () {
  213.             var id = this.get_param("id");
  214.             $.ajax({
  215.                url: "/video/" + id,
  216.                type: "get",
  217.                context: this, // 将vue传入ajax作为this对象
  218.                success: function (result, status, xhr) { //请求成功后的处理函数
  219.                   this.video = result;
  220.                   // alert("获取结果成功!");
  221.                }
  222.             })
  223.          },
  224.          update_video: function () {
  225.             $.ajax({
  226.                type: "put",
  227.                url: "/video/" + this.video.id,
  228.                data: JSON.stringify(this.video),
  229.                context: this,
  230.                success: function (result, status, xhr) {
  231.                   alert("修改视频信息成功!");
  232.                   window.location.reload();
  233.                }
  234.             })
  235.          },
  236.          delete_video: function () {
  237.             $.ajax({
  238.                type: "delete",
  239.                url: "/video/" + this.video.id,
  240.                data: JSON.stringify(this.video),
  241.                context: this,
  242.                success: function (result, status, xhr) {
  243.                   alert("删除视频成功!");
  244.                   window.location.href="/index.html";
  245.                }
  246.             })
  247.          }
  248.       }
  249.    });
  250.    app.get_video();
  251. </script>
  252. </html>
复制代码
九、项目总结



  • 项目名称:视频共享点播体系
  • 项目功能:搭建一个共享点播体系,服务器支持用户通过前端欣赏器访问服务器,获取展示与观看和操纵的界面,最
    终实现视频的上传以及观看和删改查等基础管理功能。
  • 开辟环境及工具: centos7.6、vim、g++、gdb、makefile、vscode等。
  • 技术特点: HTTP 服务器搭建, RESTful 风格接口计划, Json 序列化,线程池, HTML+CSS+JS 基础。
  • 项目模块:
    1.数据管理模块:基于 MYSQL 举行数据管理,封装数据管理类举行数据同一访问
  • 2.业务处理模块:基于 HTTPLIB 搭建 HTTP 服务器,使用 restful风格 举行接口计划处理客户端业务请求
    3.前端界面模块:基于基础的 HTML+CSS+JS 完成基于简单模板前端界面的修改与功能完成。
  • 项目扩展方向:
    1.添加用户管理以及视频分类管理
    2.添加视频的批评,打分功能。
    3.服务器上的视频或图片接纳压缩处理节省空间,举行热门与非热门的管理
  • 源码:fengxi740/学习项目



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

乌市泽哥

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表