使用C++和libcurl库实现HTTP请求(GET、POST、文件上传)

在现代软件开发中,与外部API服务进行通信已成为常见需求。本文将展示如何使用C++和libcurl库实现基本的HTTP请求,包括GET请求、POST请求(带JSON数据)以及包含文件上传的POST请求。

准备工作

首先,需要确保已安装libcurl库,并正确链接到项目中。如果还没有安装libcurl,可以在libcurl官网找到安装方法。使用CMake构建项目时,可以在CMakeLists.txt中指定库路径,确保CURL能够找到:

# set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/libcurl/share/curl")  # 替换为 CURL 安装的实际路径
find_package(CURL REQUIRED)
target_link_libraries(your_project_name PRIVATE CURL::libcurl)

基本结构

代码的核心是三个函数,分别用于GET请求、带JSON的POST请求和包含文件上传的POST请求。每个函数使用了libcurl的不同配置来处理具体需求。

代码结构和功能实现

1. req_reply:处理请求响应的函数

这个函数是libcurl的回调函数,用于处理从服务器返回的数据,将数据写入到指定的字符串流中。

size_t req_reply(void *ptr, size_t size, size_t nmemb, void *stream) {
    string *str = (string *) stream;
    (*str).append((char *) ptr, size * nmemb);
    return size * nmemb;
}
2. GET 请求

GET请求是最常用的请求类型,用于从服务器获取资源。curl_get_req函数实现了GET请求,通过curl_easy_setopt函数来设置URL和请求参数。

CURLcode curl_get_req(const std::string &url, std::string &response) {
    CURL *curl = curl_easy_init();
    CURLcode res;
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);  // 忽略SSL证书验证
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &response);
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }
    return res;
}
3. 发送带JSON数据的POST请求

POST请求通常用于将数据发送到服务器,尤其是RESTful API。curl_post_json函数可以发送包含JSON数据的POST请求,代码通过设置Content-Type头为application/json来指定数据类型。

CURLcode curl_post_json(const string &url, const string &jsonData, string &response) {
    CURL *curl = curl_easy_init();
    CURLcode res;
    if (curl) {
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: application/json");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_POST, 1);
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &response);
        res = curl_easy_perform(curl);
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
    return res;
}
4. 发送包含JSON和文件的POST请求

在某些情况下,我们可能需要同时发送JSON数据和文件。这里用curl_mime实现多部分表单上传。curl_mime允许将JSON数据和文件字段一起打包并发送给服务器。

CURLcode curl_post_with_file(const string &url, const string &jsonData, const string &filePath, string &response) {
    CURL *curl = curl_easy_init();
    CURLcode res;
    if (curl) {
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: multipart/form-data");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

        // 创建 MIME 表单
        curl_mime *form = curl_mime_init(curl);

        // 添加 JSON 数据字段
        curl_mimepart *jsonField = curl_mime_addpart(form);
        curl_mime_name(jsonField, "json");
        curl_mime_data(jsonField, jsonData.c_str(), CURL_ZERO_TERMINATED);
        curl_mime_type(jsonField, "application/json");

        // 添加文件字段
        curl_mimepart *fileField = curl_mime_addpart(form);
        curl_mime_name(fileField, "file");
        curl_mime_filedata(fileField, filePath.c_str());
        curl_mime_type(fileField, "image/png");

        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &response);
        res = curl_easy_perform(curl);

        curl_mime_free(form);
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
    return res;
}

测试用例

最后,我们在main函数中展示了三个测试用例,分别是GET请求、带JSON数据的POST请求和带文件的POST请求。

#include <iostream>
#include <string>
#include "curl/curl.h"

using namespace std;

// 请求的回复处理函数
size_t req_reply(void *ptr, size_t size, size_t nmemb, void *stream) {
    string *str = (string *) stream;
    (*str).append((char *) ptr, size * nmemb);
    return size * nmemb;
}

// HTTP GET 请求
CURLcode curl_get_req(const std::string &url, std::string &response) {
    CURL *curl = curl_easy_init();
    CURLcode res;
    if (curl) {
        // 设置 URL 地址
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        // 设置忽略 SSL 证书验证
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
        // 设置回复处理函数
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &response);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        curl_easy_setopt(curl, CURLOPT_HEADER, 1);
        // 设置连接和响应超时时间
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
        res = curl_easy_perform(curl);  // 执行 GET 请求
        curl_easy_cleanup(curl);  // 清理 curl 资源
    }
    return res;
}

// 发送带有 JSON 数据的 HTTP POST 请求
CURLcode curl_post_json(const string &url, const string &jsonData, string &response) {
    CURL *curl = curl_easy_init();
    CURLcode res;
    if (curl) {
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: application/json"); // 设置 JSON 类型头部
        //    headers = curl_slist_append(headers, ("appcode: " + appcode).c_str());
        //    headers = curl_slist_append(headers, ("User-Agent: " + agent).c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

        // 设置 POST 请求参数
        curl_easy_setopt(curl, CURLOPT_POST, 1);
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData.c_str());
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &response);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        curl_easy_setopt(curl, CURLOPT_HEADER, 1);
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
        res = curl_easy_perform(curl);  // 执行 POST 请求

        curl_slist_free_all(headers); // 释放头部内存
        curl_easy_cleanup(curl);  // 清理 curl 资源
    }
    return res;
}

// 发送包含 JSON 数据和文件的 POST 请求
CURLcode curl_post_with_file(const string &url, const string &jsonData, const string &filePath, string &response) {
    CURL *curl = curl_easy_init();
    CURLcode res;
    if (curl) {
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: multipart/form-data"); // 设置多部分表单头部
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

        // 创建 MIME 表单
        curl_mime *form = curl_mime_init(curl);

        // 添加 JSON 数据字段
        curl_mimepart *jsonField = curl_mime_addpart(form);
        curl_mime_name(jsonField, "json");
        curl_mime_data(jsonField, jsonData.c_str(), CURL_ZERO_TERMINATED);
        curl_mime_type(jsonField, "application/json");

        // 添加文件字段
        curl_mimepart *fileField = curl_mime_addpart(form);
        curl_mime_name(fileField, "file");
        curl_mime_filedata(fileField, filePath.c_str());
        curl_mime_type(fileField, "image/png");  // 设置文件类型为 image/png

        // 设置 CURL 参数
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &response);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);

        // 执行请求
        res = curl_easy_perform(curl);

        // 清理 MIME 表单和头部
        curl_mime_free(form);
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
    return res;
}

int main() {
    curl_global_init(CURL_GLOBAL_ALL);

    // 测试 GET 请求
    string getUrlStr = "http://cn.bing.com/images/trending?form=Z9LH";
    string getResponseStr;
    auto res = curl_get_req(getUrlStr, getResponseStr);
    if (res != CURLE_OK)
        cerr << "GET 请求失败: " + string(curl_easy_strerror(res)) << endl;
    else
        cout << getResponseStr << endl;

    // JSON POST 请求
    string postUrlStr = "https://api.example.com/endpoint";
    string jsonData = R"({"key1": "value1", "key2": "value2"})";
    string postResponseStr;
    res = curl_post_json(postUrlStr, jsonData, postResponseStr);
    if (res != CURLE_OK)
        cerr << "POST 请求失败: " + string(curl_easy_strerror(res)) << endl;
    else
        cout << postResponseStr << endl;

    // JSON 和文件的 POST 请求
    string postfileStr = "https://api.example.com/upload";
    string filePath = "path/to/image.png";  // 指定上传的文件路径

    res = curl_post_with_file(postfileStr, jsonData, filePath, postResponseStr);
    if (res != CURLE_OK)
        cerr << "文件 POST 请求失败: " + string(curl_easy_strerror(res)) << endl;
    else
        cout << "服务器响应:\n" << postResponseStr << endl;
    // 清空
    curl_global_cleanup();
    system("pause");
    return 0;
}

总结

使用libcurl实现HTTP请求是与外部API或服务器进行通信的强大工具。本文展示了如何发送GET请求、带JSON数据的POST请求以及包含文件的POST请求,涵盖了许多实际应用中的需求。通过合理配置libcurl选项,可以轻松实现高效且可靠的网络请求。

### 实现 Java 后端调用 GPT 接口并返回带有打字机效果的响应 为了实现这一功能,可以分为以下几个部分来描述解决方案: #### 1. 调用 GPT API 的后端逻辑 在 Java 中可以通过 HTTP 客户端(如 `HttpClient` 或 `OkHttp`)向 GPT 提供的 RESTful API 发送请求。以下是基于 OpenAI GPT-3 API 的示例代码[^1]。 ```java import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GptApiCaller { private static final String OPENAI_API_KEY = "your-api-key"; private static final String ENDPOINT_URL = "https://api.openai.com/v1/completions"; public String callGpt(String prompt) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(ENDPOINT_URL)) .header("Authorization", "Bearer " + OPENAI_API_KEY) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(""" {"model":"text-davinci-003","prompt":"%s","max_tokens":50} """.formatted(prompt))) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); return parseResponse(response.body()); // 解析 JSON 响应中的实际文本数据 } private String parseResponse(String responseBody) { // 使用 JSON 解析字符串 (e.g., Jackson or Gson),提取 'choices.text' return null; // 返回处理后的文本 } } ``` 上述代码展示了如何通过 POST 请求发送提示给 GPT 并获取其完成的结果[^2]。 --- #### 2. 将结果以流的形式传输至前端 为了让前端能够实时接收到字符级的数据更新,可以采用 WebSocket 技术或者 Server-Sent Events (SSE)[^3] 来实现实时通信。这里提供一种 SSE 方法作为例子: ##### 配置 Spring Boot 控制器支持 SSE 输出 如果使用的是 Spring Boot,则可以在控制器方法上启用事件流模式如下所示: ```java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @RestController @RequestMapping("/stream") public class TypewriterController { @GetMapping(value = "/gpt-typewriter", produces = "text/event-stream") public SseEmitter streamTypewriterEffect() { SseEmitter emitter = new SseEmitter(); try { StringBuilder sb = new StringBuilder(); // 存储完整的 GPT 响应 // 模拟逐字符推送过程 char[] chars = getGptResponse().toCharArray(); // 获取来自 GPT 的完整响应 for (char c : chars) { Thread.sleep(75); // 创建延迟模拟打字速度 sb.append(c); emitter.send(SseEmitter.event().data(sb.toString())); // 发送当前累积的内容 } emitter.complete(); // 结束流 } catch (Exception e) { emitter.completeWithError(e); // 处理错误情况 } return emitter; } private String getGptResponse() throws Exception { // 这里调用之前定义好的 GptApiCaller 类的方法 return new GptApiCaller().callGpt("Your Prompt Here"); } } ``` 此片段实现了服务器端将 GPT 文本逐步推送给客户端的功能[^4]。 --- #### 3. 前端接收与展示 最后,在前端页面中订阅该事件源即可显示动态加载的效果。下面是一个简单的 HTML JavaScript 片段用于演示目的: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>GPT Typewriter</title> </head> <body> <div id="output"></div> <script type="text/javascript"> const eventSource = new EventSource('/stream/gpt-typewriter'); eventSource.onmessage = function(event) { document.getElementById('output').innerText = event.data; }; // 错误处理机制可选加入 eventSource.onerror = () => console.error('Event Source Failed'); </script> </body> </html> ``` 这样就完成了整个流程的设计实施说明[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值