cpp-httplib库的基本使用及文件上传下载

2023-09-20 14:49:47

0.前言

cpp-httplib 是一个 header-only 的跨平台 HTTP/HTTPS 网络库,采用 MIT 开源协议,接口为阻塞调用。虽然功能简单,但也不用一堆依赖,C++ 的接口也比 libcurl 的 C 接口用起来顺手一点,文档也提供了不少参考代码,如果是做简单的 HTTP server 或者 client 还是值得一试的。

库里有两个重要的宏:

关闭异常:CPPHTTPLIB_NO_EXCEPTIONS,默认遇到错误逻辑是抛异常,可以通过该宏来关闭,但是关了之后没有别的机制来获知这个错误。

启用 OpenSSL 支持:CPPHTTPLIB_OPENSSL_SUPPORT,需要 OpenSSL 的库可以被链接,目前仅支持 1.1.1 和 3.0 版本。

还有个问题是,该库仅支持 HTTP 1.0 和 1.1,默认 1.1,如果要用 HTTP2 或 HTTP3 用别的库吧。

库地址:https://github.com/yhirose/cpp-httplib

本文代码: https://github.com/gongjianbo/MyTestCode/tree/master/Qt/TestQt_20230913_HttpLib

(为方便操作,用 Qt 库拖了个简单的界面)

1.基本使用

cpp-httplib 提供了 RESTful 风格的接口,操作起来比 libcurl 方便不少。

服务端:

    httplib::Server svr;
    svr.set_error_handler([](const httplib::Request &req, httplib::Response &res) {
        std::cerr << "Server-error:" << req.method << "\t" << req.path << "\tstatus:" << res.status << std::endl;
        std::string json = R"({"message":"path error"})";
        res.set_content(json, "text/plain");
    });
    // 路由处理抛了异常,会被捕获,然后进入 set_error_handler 逻辑
    svr.Get("/hello", [](const httplib::Request &req, httplib::Response &res) {
        std::cerr << "Server-log:" << req.method << "\t" << req.path << "\t" << req.body << std::endl;
        // 获取 url 中的参数
        for (auto it = req.params.begin(); it != req.params.end(); it++)
        {
            std::cerr << "\t" << it->first << ": " << it->second << std::endl;
        }
        res.set_content(R"({"message":"get result"})", "appliation/json");
    });
    svr.Post("/hello", [](const httplib::Request &req, httplib::Response &res) {
        std::cerr << "Server-log:" << req.method << "\t" << req.path << "\t" << req.body << std::endl;
        res.set_content(R"({"message":"post result"})", "appliation/json");
    });
    svr.listen("localhost", 12345);

listen 之后就阻塞住循环处理请求了。

客户端:

    httplib::Client cli("localhost", 12345);
    // auto res = cli.Get("hello?argkey=agrvalue");
    auto res = cli.Post("hello", R"({"formkey":"formvalue"})", "application/json");
    if (res) {
        std::cerr << "Status Code:" << res->status << std::endl;
        std::cerr << "Header:\n";
        for (auto it = res->headers.begin(); it != res->headers.end(); it++)
        {
            std::cerr << "\t" << it->first << ": " << it->second << std::endl;
        }
        std::cerr << "Body:\n" << res->body << std::endl;
    }

接口有多个重载,可根据需求选用,作者提供了大量的示例。

2.文件上传下载

文件的上传有两种方式,小文件可以一次性读写,大文件可以用 Provider 流式读写。

服务端:

// 文件上传
svr.Post("/upload", [](const httplib::Request &req, httplib::Response &res, const httplib::ContentReader &content_reader) {
    std::cerr << "Server-log: upload\t" << req.get_header_value("Content-Type") << std::endl;
    // 二进制数据可以用:multipart/form-data 和 application/octet-stream
    if (req.is_multipart_form_data()) {
        httplib::MultipartFormDataItems files;
        // 先拿到 file 信息,再流式读取
        content_reader(
            [&](const httplib::MultipartFormData &file) {
                files.push_back(file);
                std::cerr << "\tupload read " << file.filename << "\t" << file.content << std::endl;
                return true;
            },
            [&](const char *data, size_t data_length) {
                files.back().content.append(data, data_length);
                std::cerr << "\tupload read:" << data_length << std::endl;
                return true;
            });
    } else {
        std::string body;
        content_reader([&](const char *data, size_t data_length) {
            body.append(data, data_length);
            std::cerr << "\tupload read:" << data_length << std::endl;
            return true;
        });
        std::cerr << "\tupload read " << body << std::endl;
    }
    res.set_content(R"({"message":"upload result"})", "appliation/json");
});
// 文件下载
svr.Get("/download/:id", [](const httplib::Request &req, httplib::Response &res) {
    std::cerr << "Server-log: download\t" << req.path_params.at("id") << "\t" << req.get_header_value("Content-Type") << std::endl;
    res.set_header("Cache-Control", "no-cache");
    res.set_header("Content-Disposition", "attachment; filename=hello.txt");
    res.set_chunked_content_provider(
        "multipart/form-data", [](size_t offset, httplib::DataSink &sink) {
            const char arr[] = "hello world";
            auto ret = sink.write(arr + offset, sizeof(arr));
            sink.done();
            std::cerr << "\tdownload write:" << sizeof(arr) << std::endl;
            return !!ret;
        });
});

 客户端:

void doUp()
{
    httplib::Client cli = httplib::Client{"localhost", 12345};

#if 0
    // 小文件一次性发送
    httplib::MultipartFormData form;
    form.name = "myfile";
    form.content = "hello world";
    form.filename = "hello.txt";
    form.content_type = "multipart/form-data";
    httplib::MultipartFormDataItems form_items;
    form_items.push_back(form);
    httplib::Result res = cli.Post("/upload", form_items);
#else
    // 大文件用流式接口
    httplib::MultipartFormDataProvider provider;
    provider.name = "myfile";
    provider.filename = "hello.txt";
    provider.provider = [&](size_t offset, httplib::DataSink &sink){
        // offset 是已发送的偏移量
        const char arr[] = "hello world";
        auto ret = sink.write(arr + offset, sizeof(arr));
        std::cerr << "Client write:" << arr << std::endl;
        // 发送完成
        sink.done();
        return !!ret;
    };
    provider.content_type = "multipart/form-data";
    httplib::MultipartFormDataProviderItems provider_items;
    provider_items.push_back(provider);
    // header 的 Content-Type 会默认设置为 multipart/form-data,且自动加上 boundary
    httplib::Result res = cli.Post("/upload", {}, {}, provider_items);
#endif
    std::cerr << __FUNCTION__ << "\tpath:/upload\tres:" << (!!res) << std::endl;
    // printResult(res);
}

void doDown()
{
    httplib::Client cli = httplib::Client{"localhost", 12345};
#if 0
    // 小文件一次性读取
    httplib::Result res = cli.Get("/download/0");
    if (res) {
        // Content-Disposition: attachment; filename=hello.txt
        std::string dispsition = res->get_header_value("Content-Disposition");
        std::cerr << dispsition << std::endl;
    }
#else
    // 大文件用流式接口
    httplib::Result res = cli.Get(
        "/download/0",
        [&](const httplib::Response &response) {
            std::cerr << "Client read:" << response.status << std::endl;
            return true;
        },
        [&](const char *data, size_t data_length) {
            std::cerr << "Client read:" << std::string(data, data_length) << std::endl;
            return true;
        });
#endif
    std::cerr << __FUNCTION__ << "\tpath:/download/0\tres:" << (!!res) << std::endl;
    // printResult(res);
}

为了方便演示,我把文件读写省去了,固定用的 "hello world" 字符串当作文件内容。实测改为文件读写也是可用的,测试用 1G 的文件可以正常收发且未损坏。

更多推荐

Web 第一步:HTTP 协议(基础)

这里是JavaWeb的开头部分!那么先解释一下吧:Web:全球广域网,也称为万维网(www),能够通过浏览器访问的网站。JavaWeb:是用Java技术来解决相关Web互联网领域的技术栈。(JavaWebB/S概述图)HTTP协议(客户端与服务器交互的一种通讯方式)所谓交互就是“请求”和“响应”。而“协议”就是双方约定

【库函数】Qt中Json的操作

参考博客:https://www.cnblogs.com/ybqjymy/p/17264853.htmlhttps://www.jb51.net/article/260149.htmhttps://blog.csdn.net/cpp_learner/article/details/118421096之前介绍了许多C++

QT实现相关功能

1、文本的保存mianwindow.h#ifndefMAINWINDOW_H#defineMAINWINDOW_H#include<QMainWindow>#include<QFontDialog>//字体对话框#include<QFont>//字体类#include<QMessageBox>//消息对话框#inclu

企业申请ISO9001体系认证审核,流程分几步走?

ISO9001质量管理体系认证流程1.前期准备工作①建立文件化的质量管理体系;②质量管理体系运行三个月以上;③至少进行过一次内部质量管理体系内审与管理评审,且内审已覆盖所有的场所和标准条款;④提供质量手册及程序文件。2.信息交流与相关人士进行信息交流,通过人员互访、电话、传真、电子部件等方式相互了解,确定实施认证的初步

Meow

环境准备操作系统:KaliLinux或者Windows工具:nmap,telnetnmap工具[Kali官网]手册地址:https://www.kali.org/tools/nmap/摘要:Nmapisautilityfornetworkexplorationorsecurityauditing.Itsupportsp

1989-2022年企业排污许可证信息库数据(24万观测值)

1989-2022年企业排污许可证信息库数据(24万观测值)1、时间:1989-2022年2、指标:企业名称、登记状态、法定代表人、注册资本、成立日期、核准日期、所属省份、所属城市、所属区县、电话、更多电话、邮箱、更多邮箱、统一社会信用代码、纳税人识别号、注册号、组织机构代码、参保人数、企业类型、所属行业、曾用名、英文

Blender 学习笔记(一)

文章目录视图的移动右侧小键摄像机跟随调整摄像机的窗口比例当前角度变成拍摄视角视图(正视图,顶视图,侧视图)物体的对焦物体的移动,旋转,放大与缩小加选框选全选添加物体物体删除视图的移动shift+鼠标中键=平移鼠标中键=旋转中键滚轮=缩放右侧小键搜索键(放大与缩小),手掌(平移),摄像机(0键进入),正交与透视视图切换(

在线旅游平台步入新时代,携程如何走出自己的路?

今年旅游从线下到线上全方位火了。有统计数据,一季度,光是抖音,旅游达人发布视频数量就高达175万条,播放量1350亿次,收获27亿次点赞。在这一趋势下,许多“不出名”的景区和酒店借势抖音达人完成“出圈”。短视频如抖音也已开始布局酒旅赛道且势头渐盛。虽然行业竞争加剧,但传统OTA平台地位似乎依旧稳如磐石。9月5日,携程发

RCP-第2章 ROS+Matlab

RCP系列文章第一章Matlab安装第2章ROS+MatlabMatlab安装RCP系列文章前言一、Matlab获取二、安装1.解压10.软件安装中(我的电脑大约需要10分钟)12.完成)三、破解1.打开安装包解压后的【R2018b(64bit)】中的【Crack】文件夹2.全选该文件夹下所有文件鼠标右击选择【复制】4

企业电子招投标采购系统——功能模块&功能描述+数字化采购管理 采购招投标

​功能描述1、门户管理:所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含:招标公告、非招标公告、系统通知、政策法规。2、立项管理:企业用户可对需要采购的项目进行立项申请,并提交审批,查看所有的立项信息。主要功能包含:招标立项申请、非招标立项申请、采购立项管理。3、采购项目管理:可对项目采购过程全流程

评价指标分类

声明本文是学习GB-T42874-2023城市公共设施服务城市家具系统建设实施评价规范.而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们1范围本文件确立了城市家具系统建设实施的评价原则、评价流程,给出了评价指标,描述了方汁.并却定了评价要求。家具系统建设实施的评价。通过文中的规成本文件必不可少的条款

热文推荐