目录
项目描述:
核心需求:
- 存储图片,实现一个http的服务器;
- 用这个服务器存储图片,针对每个图片提供唯一的url,根据url将图片放在网页上
项目内容:
- url:有一个url来表示图片的位置,
- 有一个image标签,里面引用这个位置
- 获取某个图片的属性
- 删除图片
整体架构:
模块划分:
模块划分:
- 数据存储模块:服务器模块给前端提供一些接口
- 数据存储模块: 数据存储方式:冯诺依曼存储器(内存:易失;外存(磁盘)):持久性存储,云端:网络;数据库:存储信息)
- 服务器模块:(给前端提供接口)
md5运用:
- md5是一种字符串哈希算法;
- 无论什么字符串,他的MD5都是固定长度,md5sum 计算一个文件的md5值:
-
[yu@localhost ~]$ md5sum a.out fb465f726b23e322267626b1965e2020 a.out
- 如果字符串内容稍有变化,得到的MD5值差异很大;
- 通过字符串很容易获得MD5值,由MD5值退出字符串理论上不可能。
- MD5 算法还可以作为一种电子签名的方法来使用,使用 MD5算法就可以为任何文件(不管其大小、格式、数量)产生一个独一无二的“数字指纹”,借助这个“数字指纹”,通过检查文件前后 MD5 值是否发生了改变,就可以知道源文件是否被改动
- 我们在下载软件的时候经常会发现,软件的下载页面上除了会提供软件的下载地址以外,还会给出一串长长的字符串。这串字符串其实就是该软件的MD5 值,它的作用就在于下载该软件后,对下载得到的文件用专门的软件(如 Windows MD5 check 等)做一次 MD5 校验,以确保我们获得的文件与该站点提供的文件为同一文件。
- 利用 MD5 算法来进行文件校验的方案被大量应用到软件下载站、论坛数据库、系统文件安全等方面 。
- md5运用作用:
- 这个字段用来进行校验图片(文件))内容正确性,
- 上传图片后,服务器计算一个该图片的md5值,
- 后续用户下载图片后,也能获取到该图片的MD5值,
- 两次md5进行对比,确定图片内容正确性,
数据库设计:
数据库设计(数据库创表和表头的信息):
- 创建一张表:
-
[yu@localhost ~]$ mkdir class [yu@localhost ~]$ cd class [yu@localhost class]$ mkdir image_server [yu@localhost class]$ cd image_server [yu@localhost image_server]$ mkdir mysql [yu@localhost image_server]$ cd mysql [yu@localhost mysql]$ vim db.sql create databases if not exists image_system; use image_system; drop table if exists image_table; create table image_table( image_id int not null primary key auto_increment, image_name varchar(256), size int, upload_time varchar(50), md5 varchar(128), type varchar(1024), path varchar(1024) ); insert into image_table values(null,'test.png',1025,'2019/09/04','aaabbbccc','png','data/test.png')
- 针对图片内容,存在磁盘上
- 数据库内容迁移,创建库创表语句写入文件,mysql客户端批量执行文件
-
> 重定向,本来输出到标椎输出上,输出到文件中
< 重定向 把标椎输入重定向到文件上
2> 重定向 标准错误重定向到文件中
-
mysql -uroot < db.sql MariaDB [image_system]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | db1 | | image_system | | performance_schema | +--------------------+ 4 rows in set (0.00 sec) MariaDB [image_system]> show tables; +------------------------+ | Tables_in_image_system | +------------------------+ | image_table | +------------------------+ 1 row in set (0.00 sec) MariaDB [image_system]> desc image_table; +-------------+---------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+---------------+------+-----+---------+----------------+ | image_id | int(11) | NO | PRI | NULL | auto_increment | | image_name | varchar(256) | YES | | NULL | | | size | int(11) | YES | | NULL | | | upload_time | varchar(50) | YES | | NULL | | | md5 | varchar(128) | YES | | NULL | | | type | varchar(128) | YES | | NULL | | | path | varchar(1024) | YES | | NULL | | +-------------+---------------+------+-----+---------+----------------+ 7 rows in set (0.10 sec) MariaDB [image_system]> select * from image_table; +----------+------------+------+-------------+--------------+------+-----------------+ | image_id | image_name | size | upload_time | md5 | type | path | +----------+------------+------+-------------+--------------+------+-----------------+ | 1 | test.png | 1024 | 2019/08/26 | aaaabbbbcccc | png | ./data/test.png | | 2 | test.png | 1024 | 2019/08/26 | abcdef | png | data/test.png | +----------+------------+------+-------------+--------------+------+-----------------+ 2 rows in set (0.01 sec) MariaDB [image_system]>
使用MySQL C API 操作数据库:
- 将查询出数据转换为JSON格式作为输出响应
- JSON是一种数据组织格式,序列化与反序列化是最为关键的内容,是一种为人类容易理解的一种格式,相对来讲牺牲了部分效率。
- 而与之对应的protobuf是谷歌出品的一种二进制序列化协议,效率很高,自然对于人类来讲很难看懂,是给计算机读的二进制文件。
- 安装MySQL C API:
yum install mysql-devel
代码中使用时需要链接上 MySQL 提供的库 :
-L /usr/lib64/mysql -lmysqlclient
查看是否成功安装
[yu@localhost mysql]$ ll mysql.h
-rw-r--r--. 1 root root 38500 Apr 20 2018 mysql.h
[yu@localhost mysql]$ cd /usr/include/mysql/
[yu@localhost mysql]$ ll mysql.h
-rw-r--r--. 1 root root 38500 Apr 20 2018 mysql.h
vim mysql_insert.cc
-
数据库插入数据:mysql_insert.cc
#include<cstdio> #include<cstdlib> #include<mysql/mysql.h> int main() { //使用mysql API来操作数据库 //1.创建一个mysql的句柄 MYSQL* mysql = mysql_init(NULL); //2.拿着句柄和数据库建立连接 // mysql_init 返回的指针 // 主机地址 // 用户名 // 密码 // 数据库名 // 端口号 // unix_socket // client_flag if(mysql_real_connect(mysql,"127.0.0.1","root", "","image_system",3306,NULL,0)==NULL){ //数据库连接失败 printf("连接失败!%s\n",mysql_error(mysql)); return 1; //3.设置编码格式,服务器编码格式默认不是utf8,安装时配置成utf8; mysql_set_character_set(mysql,"utf8"); //4.拼接SQL语句 char sql[4096]={0}; char image_name[] = "滑稽.jpg"; int size = 16 * 1024; char upload_time[] = "2019/05/14"; char md5[] = "123456"; char content_type[] = "jpg"; char path[] = "./滑稽.jpg"; sprintf(sql,"insert into image_table values(null,'test.png',1024 //5.执行sql语句,负责客户端给服务端发送数据的过程 int ret = mysql_query(mysql,sql); if(ret!=0){ printf("执行sql失败!%s\n",mysql_error(mysql)); return 1; //6.关闭句柄 mysql_close(mysql); return 0; } } return 0; }
usr==>unix shared resoutce; unix 共享库
测试写的代码能否运行,写一个Makefile编译
[yu@localhost mysql]$ vim makefile 1 mysql_insert:mysql_insert.cc 2 gcc $^ -o $@ -L/usr/lib/mysql -lmysqlclient 3 .PHONY:clean 4 clean: 5 rm mysql_insert ~ [yu@localhost mysql]$ cd /usr/lib64/ [yu@localhost lib64]$ cd mysql/ [yu@localhost mysql]$ ll -rwxr-xr-x. 1 root root 3135712 Aug 16 2018 libmysqlclient.so.18.0.0 //.so 动态库 [yu@localhost ~]$ cd class [yu@localhost class]$ cd image_server [yu@localhost image_server]$ cd mysql/ [yu@localhost mysql]$ vim makefile [yu@localhost mysql]$ make [yu@localhost mysql]$ ll [yu@localhost mysql]$ ./mysql_insert
-
数据库查找数据:mysql_select.cc
// 1. 初始化句柄 // 2. 建立连接 // 3. 设置编码格式 // 4. 拼装 SQL 语句 // 5. 执行 SQL 语句 // 6. 遍历查询结果 // 7. 释放结果集 // 8. 关闭句柄 #include <cstdio> #include <cstdlib> #include <mysql/mysql.h> int main() { // 1. 初始化句柄 MYSQL* connect_fd = mysql_init(NULL); // 2. 建立链接 // mysql_init 返回的指针 // 主机地址 // 用户名 // 密码 // 数据库名 // 端口号 // unix_socket // client_flag if (mysql_real_connect(connect_fd, "127.0.0.1", "root", "", "image_system", 3306, NULL, 0) == NULL) { printf("连接失败! %s\n", mysql_error(connect_fd)); return 1; } // 3. 设置编码格式 mysql_set_character_set(connect_fd, "utf8"); // 4. 拼装 SQL 语句 char sql[1024 * 4] = {0}; sprintf(sql, "select * from image_table"); // 5. 执行 SQL 语句 int ret = mysql_query(connect_fd, sql); if (ret < 0) { printf("执行 sql 失败! %s\n", mysql_error(connect_fd)); return 1; } // 6. 遍历查询结果 MYSQL_RES* result = mysql_store_result(connect_fd); if (result == NULL) { printf("获取结果失败! %s\n", mysql_error(connect_fd)); return 1; } // a) 获取行数和列数 int rows = mysql_num_rows(result); int fields = mysql_num_fields(result); printf("rows: %d, fields: %d\n", rows, fields); // b) 打印结果 for (int i = 0; i < rows; ++i) { MYSQL_ROW row = mysql_fetch_row(result); for (int j = 0; j < fields; ++j) { printf("%s\t", row[j]); } printf("\n"); } // 7. 释放结果集 mysql_free_result(result); // 8. 关闭句柄 mysql_close(connect_fd); printf("执行成功!\n"); return 0; }
连接数据库中可能会产生的问题
数据的字符编码问题
解决方法:
修改编码方式
mysql_query(_mySql, “set names ‘gbk’”); -
(1)修改 mysql安装根目录下的my.ini(MySQL Server Instance Configuration 文件), (2)设置default-character-set=gbk(注意,有2处) (3)在data目录下,打开相应数据库的文件,找到db.opt配置文件 (4)设置default-character-set=gbk default-collation=gbk_chinese_ci 重启mysql就好了
服务器API设计:
1.API定义:
1)API 是用于构建应用程序软件的一组子程序定义,协议和工具。一套明确定义的各种软件组件之间的通信方法,实现和其他软件快速交互。
2)API 应用范围很广:从操作系统中简单的 fork() 到我们接触的百度地图API,和风天气API等
1.API功能:
1)API确认所有操作都是合法的
2)在发生错误时,API会根据错误报告机制发出指示
3)API内置授权和访问控制,确保只有授权人员才能访问特殊数据
4)可以充当防火墙,防止资源被滥用,允许合法的请求
http请求中的8种请求方法
1、opions 返回服务器针对特定资源所支持的HTML请求方法 或web服务器发送*测试服务器功能(允许客户端查看服务器性能)
2、Get 向特定资源发出请求(请求指定页面信息,并返回实体主体)
3、Post 向指定资源提交数据进行处理请求(提交表单、上传文件),又可能导致新的资源的建立或原有资源的修改
4、Put 向指定资源位置上上传其最新内容(从客户端向服务器传送的数据取代指定文档的内容)
5、Head 与服务器索与get请求一致的相应,响应体不会返回,获取包含在小消息头中的原信息(与get请求类似,返回的响应中没有具体内容,用于获取报头)
6、Delete 请求服务器删除request-URL所标示的资源*(请求服务器删除页面)
7、Trace 回显服务器收到的请求,用于测试和诊断
8、Connect HTTP/1.1协议中能够将连接改为管道方式的代理服务器
http服务器至少能实现get、head、post方法,其他都是可选的
-
新增图片:
请求: POST /image Content-Type: application/x-www-form-urlencoded ------WebKitFormBoundary5muoelvEmAAVUyQB Content-Disposition: form-data; name="filename"; filename="图标.jpg" Content-Type: image/jpeg ......[图片正文]..... 响应: HTTP/1.1 200 OK { "ok": true, }
-
查看所有图片元信息:
请求: GET /image/ HTTP/1.1 200 OK [ { "image_id": 1, "image_name": "1.png", "content_type": "image/png", "md5": "[md5值]" } ]
-
查看指定图片元信息 :
请求: GET /image/:image_id 响应: HTTP/1.1 200 OK { "image_id": 1, "image_name": "1.png", "content_type": "image/png", "md5": "[md5值]" }
-
查看图片内容:
请求: GET /image/show/:image_id 响应: HTTP/1.1 200 OK content-type: image/png [响应 body 中为 图片内容 数据]
-
删除图片:
请求: DELETE /image/:image_id 响应: HTTP/1.1 200 OK { "ok": true }
服务端实现:
-
封装数据库操作:
接口设计
db.hpp
namespace image_system { static MYSQL* MySQLInit() {} static void MySQLRelease(MYSQL* mysql) {} class ImageTable { ImageTable(MYSQL* mysql) { } bool Insert(const Json::Value& image); bool SelectAll(Json::Value* images); bool SelectOne(int32_t image_id, Json::Value* image); bool Delete(int image_id); }; }
各个接口代码实现
db.hpp
// 这个文件相当于 model 层. // 只进行数据的基本 CURD , 不涉及到更复杂的数据加工 #pragma once #include <cstdlib> #include <cstring> #include <stdint.h> #include <string> #include <memory> #include <mysql/mysql.h> #include <jsoncpp/json/json.h> namespace image_system { static MYSQL* MySQLInit() { MYSQL* connect_fd = mysql_init(NULL); if (mysql_real_connect(connect_fd, "127.0.0.1", "root", "", "image_system", 3306, NULL, 0) == NULL) { printf("连接失败! %s\n", mysql_error(connect_fd)); return NULL; } mysql_set_character_set(connect_fd, "utf8"); return connect_fd; } static void MySQLRelease(MYSQL* mysql) { mysql_close(mysql); } class ImageTable { public: ImageTable(MYSQL* mysql) : mysql_(mysql) { } bool Insert(const Json::Value& image) { char sql[4096] = {0}; sprintf(sql, "insert into image_table values(null, '%s', %d, '%s','%s', '%s', '%s')", image["name"].asCString(), image["size"].asInt(), image["upload_time"].asCString(), image["md5"].asCString(), image["content_type"].asCString(), image["path"].asCString()); int ret = mysql_query(mysql_, sql); if (ret != 0) { printf("执行 sql 失败! sql=%s, %s\n", sql, mysql_error(mysql_)); return false; } return true; } bool SelectAll(Json::Value* images) { char sql[1024 * 4] = {0}; // 可以根据 tag_id 来筛选结果 sprintf(sql, "select * from image_table"); int ret = mysql_query(mysql_, sql); if (ret != 0) { printf("执行 sql 失败! %s\n", mysql_error(mysql_)); return false; } MYSQL_RES* result = mysql_store_result(mysql_); if (result == NULL) { printf("获取结果失败! %s\n", mysql_error(mysql_)); return false; } int rows = mysql_num_rows(result); for (int i = 0; i < rows; ++i) { MYSQL_ROW row = mysql_fetch_row(result); Json::Value image; image["image_id"] = atoi(row[0]); image["image_name"] = row[1]; image["size"] = atoi(row[2]); image["upload_time"] = row[3]; image["md5"] = row[4]; image["content_type"] = row[5]; image["path"] = row[6]; images->append(image); } return true; } bool SelectOne(int32_t image_id, Json::Value* image) { char sql[1024 * 4] = {0}; sprintf(sql, "select * from image_table where image_id = %d", image_id); int ret = mysql_query(mysql_, sql); if (ret != 0) { printf("执行 sql 失败! %s\n", mysql_error(mysql_)); return false; } MYSQL_RES* result = mysql_store_result(mysql_); if (result == NULL) { printf("获取结果失败! %s\n", mysql_error(mysql_)); return false; } int rows = mysql_num_rows(result); if (rows != 1) { printf("查找结果不为 1 条. rows = %d!\n", rows); return false; } MYSQL_ROW row = mysql_fetch_row(result); (*image)["image_id"] = atoi(row[0]); (*image)["name"] = row[1]; (*image)["size"] = atoi(row[2]); (*image)["upload_time"] = row[3]; (*image)["md5"] = row[4]; (*image)["content_type"] = row[5]; (*image)["path"] = row[6]; return true; } bool Delete(int image_id) { char sql[1024 * 4] = {0}; sprintf(sql, "delete from image_table where image_id=%d", image_id); int ret = mysql_query(mysql_, sql); if (ret != 0) { printf("执行 sql 失败! sql=%s, %s\n", sql, mysql_error(mysql_)); return false; } return true; } private: MYSQL* mysql_; }; } // end blog_system
-
使用 JSON 作为数据交互
json 出自 JavaScript, 是一种非常方便的键值对数据组织格式, 目前被业界广泛使用.
C++ 中可以使用 jsoncpp 这个库来解析和构造 json 数据
yum install jsoncpp-devel
-
测试数据库操作
// 实现一个数据库接口测试程序, 用来验证前面的数据库接口是否正确 #include <iostream> #include "db.hpp" using namespace image_system; void TestImageTable() { bool ret = false; // 更友好的格式化显示 Json Json::StyledWriter writer; MYSQL* mysql = MySQLInit(); Json::Value image; image["name"] = "滑稽.jpg"; image["size"] = 16 * 1024; image["upload_time"] = "2019/01/01"; image["md5"] = "987654321"; image["content_type"] = "image/jpg"; image["path"] = "./滑稽.jpg"; std::cout << "==============测试插入=====================" << std::endl; ImageTable image_table(mysql); ret = image_table.Insert(image); std::cout << "Insert: " << ret << std::endl; std::cout << "==============测试查找=====================" << std::endl; Json::Value images; ret = image_table.SelectAll(&images); std::cout << "SelectAll: " << ret << std::endl << writer.write(images) << std::endl; Json::Value image_out; ret = image_table.SelectOne(1, &image_out); std::cout << "SelectOne: " << ret << std::endl << writer.write(image_out) << std::endl; std::cout << "==============测试删除=====================" << std::endl; int image_id = 2; ret = image_table.Delete(image_id); std::cout << "Delete: " << ret << std::endl; MySQLRelease(mysql); } int main() { TestImageTable(); return 0; }
Makefifile
FLAGS=-std=c++11 -L/usr/lib64/mysql -lmysqlclient -ljsoncpp -lpthread -g .PHONY:all all:db_test db_test:db_test.cc db.hpp g++ db_test.cc -o db_test $(FLAGS) .PHONY:clean clean: rm db_test
-
服务器基本框架
使用 cpp-httplib
#include "httplib.h" int main() { using namespace httplib; Server server; server.Get("/", [](const Request& req, Response& resp) { (void)req; resp.set_content("<html>hello</html>", "text/html"); }); server.set_base_dir("./wwwroot"); server.listen("0.0.0.0", 9094); return 0; }
g++ main.cc -lpthread -std=c++11
/// // 构建 HTTP 服务器提供约定的 API 接口 /// #include <signal.h> #include <jsoncpp/json/json.h> #include "util.hpp" #include "db.hpp" #include "httplib.h" #include <openssl/md5.h> std::string StringMD5(const std::string& str); const std::string base_path = "./image_data/"; MYSQL* mysql = NULL; int main() { using namespace httplib; using namespace image_system; Server server; // 1. 数据库客户端初始化和释放 mysql = MySQLInit(); signal(SIGINT, [](int) { MySQLRelease(mysql); exit(0);}); ImageTable image_table(mysql); // 2. 先按照 cpp-httplib 的文档演示基本的图片上传处理过程 server.Post("/image_test", [](const Request& req, Response& resp) { }); // 3. 新增图片. server.Post("/image", [&image_table](const Request& req, Response& resp) { }); // 4. 查看所有图片的元信息 server.Get("/image", [&image_table](const Request& req, Response& resp) { }); // 5. 查看图片元信息 // raw string(c++ 11), 转义字符不生效. 用来表示正则表达式正好合适 // 关于正则表达式, 只介绍最基础概念即可. \d+ 表示匹配一个数字 // http://help.locoy.com/Document/Learn_Regex_For_30_Minutes.htm server.Get(R"(/image/(\d+))", [&image_table](const Request& req, Response& resp) { }); // 6. 查看图片内容 server.Get(R"(/image/show/(\d+))", [&image_table](const Request& req, Response& resp) { }); // 设置静态文件目录 server.set_base_dir("./wwwroot"); server.listen("0.0.0.0", 9094); return 0; } // 需要包含头文件 // #include <openssl/md5.h> // Makefile 需要 -lcrypto std::string StringMD5(const std::string& str) { const int MD5LENTH = 16; unsigned char MD5result[MD5LENTH]; // 调用 openssl 的函数计算 md5 MD5((const unsigned char*)str.c_str(),str.size(),MD5result); // 转换成字符串的形式方便存储和观察 char output[1024] = {0}; int offset = 0; for (int i = 0; i < MD5LENTH; ++i) { offset += sprintf(output + offset, "%x", MD5result[i]); } return std::string(output); }
-
测试上传图片
server.Post("/image_test", [](const Request& req, Response& resp) { auto size = req.files.size(); bool ret = req.has_file("filename"); const auto& file = req.get_file_value("filename"); // file.filename // file.content_type auto body = req.body.substr(file.offset, file.length); LOG(INFO) << "size: " << size << ", ret: " << ret << ", " << file.filename << ", " << file.content_type << ", " << file.offset << ", " << file.length << std::endl; FileUtil::WriteFile(file.filename, req.body.substr(file.offset, file.length)); resp.set_content("ok", "text/html"); });
实现一个测试页面 upload.html, 放到 wwwroot 目录中
-
一个HTTP服务器的作用是接收到http请求,并根据请求返回相应的http响应。
在HTTP协议中,用户自定制携带一些信息上传给服务器有多种方法,一种比较传统的方法是借助URL中query_string查询字符串来携带一些信息。
那么我们可以直接使用浏览器构造http请求。
我们上传图片借助一个HTML文件,在这里我们依旧使用别人封装好的简单的上传方法。
我们的http请求都使用9090端口,在使用前我们先查看一下端口有没有被占用,具体命令:<html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> </head> <body> <form method="POST" enctype="multipart/form-data" action="http://47.98.116.42:9094/image"> <table> <tr> <td>文件上传:</td> <td><input type="file" name="filename"/></td> </tr> <tr> <td></td> <td><input type="submit" value="上传"/></td> </tr> </table> </form> </body> </html>
form标签表示这是一个form表单,form表单是一种传统的浏览器、网页和服务器交互的方式,其功能就是提供一些选项框,借助这些选项框将数据提交给服务器。
-
接口实现
实现完整上传图片接口
// 3. 新增图片.
server.Post("/image", [&image_table](const Request& req, Response& resp) {
Json::FastWriter writer;
Json::Value resp_json;
LOG(INFO) << "新增图片" << std::endl;
// 1. 进行参数校验
bool ret = req.has_file("filename");
if (!ret) {
resp_json["ok"] = false;
resp_json["reason"] = "req has no filename field!";
resp.status = 400;
resp.set_content(writer.write(resp_json), "application/json");
return;
}
// 2. 构造 Json 格式数据, 并调用数据层接口插入数据
const auto& file = req.get_file_value("filename");
const std::string& image_body = req.body.substr(file.offset, file.length);
Json::Value image;
image["name"] = file.filename;
image["size"] = (int)file.length;
image["upload_time"] = TimeUtil::FormatTime();
image["md5"] = StringMD5(image_body);
image["content_type"] = file.content_type;
// 为了防止重复, 用 md5 作为文件名更稳妥
image["path"] = base_path + file.filename;
ret = image_table.Insert(image);
if (!ret) {
resp_json["ok"] = false;
resp_json["reason"] = "insert db failed!";
resp.status = 500;
resp.set_content(writer.write(resp_json), "application/json");
return;
}
// 3. 保存文件实体
FileUtil::WriteFile(image["path"].asString(), image_body);
// 4. 构造响应
resp_json["ok"] = true;
resp.set_content(writer.write(resp_json), "text/html");
});
实现查看所有图片元信息接口
// 4. 查看所有图片的元信息
server.Get("/image", [&image_table](const Request& req, Response& resp) {
(void) req;
Json::Reader reader;
Json::FastWriter writer;
Json::Value resp_json;
LOG(INFO) << "查看所有图片信息: " << std::endl;
// 1. 调用数据库接口查询数据
Json::Value images;
bool ret = image_table.SelectAll(&images);
if (!ret) {
resp_json["ok"] = false;
resp_json["reason"] = "SelectAll failed!\n";
resp.status = 500;
resp.set_content(writer.write(resp_json), "application/json");
return;
}
// 2. 构造响应结果
resp.set_content(writer.write(images), "application/json");
return;
});
实现查看单个图片元信息接口
// 5. 查看图片元信息
// raw string(c++ 11), 转义字符不生效. 用来表示正则表达式正好合适
// 关于正则表达式, 只介绍最基础概念即可. \d+ 表示匹配一个数字
// http://help.locoy.com/Document/Learn_Regex_For_30_Minutes.htm
server.Get(R"(/image/(\d+))", [&image_table](const Request& req, Response& resp) {
Json::Reader reader;
Json::FastWriter writer;
Json::Value resp_json;
// 1. 获取到图片 id
int image_id = std::stoi(req.matches[1]);
LOG(INFO) << "查看图片信息: " << image_id << std::endl;
// 2. 调用数据库接口查询数据
Json::Value image;
bool ret = image_table.SelectOne(image_id, &image);
if (!ret) {
resp_json["ok"] = false;
resp_json["reason"] = "SelectOne failed!\n";
resp.status = 500;
resp.set_content(writer.write(resp_json), "application/json");
return;
}
// 3. 构造响应结果
resp.set_content(writer.write(image), "application/json");
return;
});
实现查看图片内容接口
// 6. 查看图片内容
server.Get(R"(/image/show/(\d+))", [&image_table](const Request& req, Response& resp) {
Json::Reader reader;
Json::FastWriter writer;
Json::Value resp_json;
// 1. 获取到图片 id
int image_id = std::stoi(req.matches[1]);
LOG(INFO) << "查看图片信息: " << image_id << std::endl;
// 2. 调用数据库接口查询数据
Json::Value image;
bool ret = image_table.SelectOne(image_id, &image);
if (!ret) {
resp_json["ok"] = false;
resp_json["reason"] = "SelectOne failed!\n";
resp.status = 500;
resp.set_content(writer.write(resp_json), "application/json");
return;
}
std::string image_body;
ret = FileUtil::ReadFile(image["path"].asString(), &image_body);
if (!ret) {
resp_json["ok"] = false;
resp_json["reason"] = "path open failed\n";
resp.status = 500;
resp.set_content(writer.write(resp_json), "application/json");
return;
}
// 3. 构造响应结果
resp.set_content(image_body, image["content_type"].asCString());
return;
});
实现删除图片接口
// 7. 删除图片
server.Delete(R"(/image/(\d+))", [&image_table](const Request& req, Response& resp) {
Json::Value resp_json;
Json::FastWriter writer;
// 1. 解析获取 blog_id
// 使用 matches[1] 就能获取到 blog_id
// LOG(INFO) << req.matches[0] << "," << req.matches[1] << "\n";
int image_id = std::stoi(req.matches[1]);
LOG(INFO) << "删除指定图片: " << image_id << std::endl;
// 2. 调用数据库接口删除博客
bool ret = image_table.Delete(image_id);
if (!ret) {
resp_json["ok"] = false;
resp_json["reason"] = "Delete failed!\n";
resp.status = 500;
resp.set_content(writer.write(resp_json), "application/json");
return;
}
// 3. 包装正确的响应
resp_json["ok"] = true;
resp.set_content(writer.write(resp_json), "application/json");
return;
});