项目介绍:使用Restful风格设计HTTP服务器,提供对图片的增删改查能力,同时搭配简单的页面辅助完成图片上传和展示。
服务器API设计:资源由URI来指定,对资源的包括包括获取、创建、修改、和删除资源,通过操作资源的表现形式来操作资源。
列如:http://ip:port/image?op=get&image_id=123
1.操作方法:GET查, POST增, PUT改, DELETE删
2.http path 表示操作的对象
3.补充信息一般使用body来传递,body中使用jison格式的数据来组织,方便进行调试
4.响应数据也是使用json格式来组织
数据库设计
1.创建图片表
create table `image_table`(image_id int not null primary key auto_increment,
image_name varchar(50),
size bigint,
upload_time varchar(50),
md5 varchar(128),
content_type varchar(50) comment '图片类型',
path varchar(1024) comment '图片所在路径');
,2.封装数据库接口设计
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);
};
}
服务器API设计
服务器框架
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.图片上传
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. 查看图片元信息
// 正则表达式, \d+ 表示匹配一个数字
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;
}
1.上传图片
请求:
POST /image
Content-Type: application/x-www-form-urlencoded
[内容]
响应:
HTTP/1.1 200 OK
{
"ok": true
}
代码实现:
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");
});
2.查看所有图片信息
请求:
GET /image/
响应:
HTTP/1.1 200 OK
[
{
"image_id": 1,
"image_name": "1.png",
"content_type": "image/png",
"md5": "[md5值]"
}
]
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;
});
3.查看指定图片信息
请求:
GET /image/:image_id
响应:
HTTP/1.1 200 OK
{
"image_id": 1,
"image_name": "1.png",
"content_type": "image/png",
"md5": "[md5值]"
}
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;
});
4.查看指定图片内容
请求:
GET /image/show/:image_id
响应:
HTTP/1.1 200 OK
content-type: image/png
[响应 body 中为 图片内容 数据]
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;
});
5.删除图片
DELETE /image/:image_id
响应:
HTTP/1.1 200 OK
{
"ok": true
}
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;
});
完整代码:https://github.com/LTXER/imageservr/tree/master/image_server/server