curl操作之Get/Post/上传文件 参数设置以及调用细节

该博客详细介绍了如何使用C++实现一个HTTP客户端,支持GET、POST和文件上传操作。通过Curl库初始化参数,设置各种HTTP请求选项,包括设置超时、SSL验证、回调函数等。同时,还展示了如何处理进度回调、读写回调以及获取请求的交互时间等信息。此外,还提供了上传文件的核心参数设置和外部调用的示例代码。

        curl 是一个强大的命令行工具,用于与 URL 进行数据传输,支持多种协议。以下是使用 curl 进行 GET、POST 请求和文件上传的详细参数设置和调用细节。

1. GET 请求

        GET 请求用于从服务器获取数据。

示例

   curl -X GET "http://example.com/api/resource?param1=value1&param2=value2"

参数说明
  • -X GET:显式指定使用 GET 方法(通常可以省略,因为 curl 默认使用 GET)。
  • URL:请求的目标 URL,可以附带查询参数。
其他常用选项
  • -H "Authorization: Bearer <token>":添加请求头,如授权信息。
  • -o output.txt:将响应内容写入文件。

2. POST 请求

        POST 请求用于向服务器提交数据。

示例

   curl -X POST "http://example.com/api/resource" -d "param1=value1&param2=value2"

参数说明
  • -X POST:显式指定使用 POST 方法。
  • -d:后面跟随数据,格式可以是 key=value 的形式。可以多次使用 -d 添加多个参数。
使用 JSON 数据

        如果要发送 JSON 数据,可以使用 -H 选项设置内容类型:curl -X POST "http://example.com/api/resource" -H "Content-Type: application/json" -d '{"param1":"value1", "param2":"value2"}'

3. 上传文件

        上传文件通常使用 POST 请求。curl -X POST "http://example.com/upload" -F "file=@/path/to/file.txt"

参数说明
  • -F:表示表单数据,后面跟随 file=@/path/to/file.txt,其中 @ 表示读取本地文件。
  • 可以同时上传多个文件:curl -X POST "http://example.com/upload" -F "file1=@/path/to/file1.txt" -F "file2=@/path/to/file2.jpg"

4. 其他常用选项

  • -H:添加自定义请求头。
  • -v:显示详细的请求和响应信息,便于调试。
  • -L:跟随重定向。
  • --data-urlencode:对数据进行 URL 编码。

5. 综合示例

以下是一个综合示例,展示如何使用 GET 和 POST 请求、上传文件以及设置请求头:

# GET 请求 curl -X GET "http://example.com/api/resource?param1=value1" # POST 请求 curl -X POST "http://example.com/api/resource" -H "Content-Type: application/json" -d '{"param1":"value1", "param2":"value2"}' # 上传文件 curl -X POST "http://example.com/upload" -F "file=@/path/to/file.txt" -H "Authorization: Bearer <token>" 

6. 注意事项

  • 确保 URL 正确,特别是在 POST 和文件上传的情况下。
  • 对于需要认证的 API,确保传递必要的认证信息。
  • 检查返回的状态码和响应内容,以确保请求成功。

        通过合理使用 curl 的各种选项,可以方便地与 HTTP 服务进行交互,进行数据获取、提交和文件上传等操作。

7. 工程代码实例

  • Curl初始化参数
    // 这个接口里面设置的参数选项已经完全覆盖了 Get、Post、HttpPost(文件上传)操作    
    int HttpClient::initCurl(const HttpClientUrlArgs &urlArgs) {
        auto headers = urlArgs.headers;
        std::vector<std::string> ips;
        if (XXX::XXX::GetInstance().GetProxyConfigInfo().m_httpDnsEnable) {
            ips = XXX::XXX::GetInstance().GetIpsByHostName(urlArgs.hostName);
        }

        if (ips.empty()) {
            m_requestUrl = XXX::SplicingUrl(urlArgs.protocol, urlArgs.hostName, urlArgs.port, urlArgs.url, urlArgs.args);
        } else {
            m_requestUrl = XXX::SplicingUrl(urlArgs.protocol, ips[RandomNum()%ips.size()], urlArgs.port, urlArgs.url, urlArgs.args);
            headers.emplace("Host", urlArgs.hostName);
        }

        if (m_pCurl != nullptr) {
            curl_easy_cleanup(m_pCurl);
            m_pCurl = nullptr;
        }
        if (m_pHeadList != nullptr) {
            curl_slist_free_all(m_pHeadList);
            m_pHeadList = nullptr;
        }

        m_pCurl = curl_easy_init();
        if (m_pCurl == nullptr) {
            return -1;
        }

        curl_easy_setopt(m_pCurl, CURLOPT_URL, m_requestUrl.c_str()); // 请求地址
        curl_easy_setopt(m_pCurl, CURLOPT_ACCEPT_ENCODING, "");
        curl_easy_setopt(m_pCurl, CURLOPT_NOSIGNAL, 1L);
        curl_easy_setopt(m_pCurl, CURLOPT_TIMEOUT, 10L);
        curl_easy_setopt(m_pCurl, CURLOPT_SSL_VERIFYHOST, 0L);
        curl_easy_setopt(m_pCurl, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, HttpClientOnWriteCallback); // 写回调:如Post、HttpPost
        curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, this);
        curl_easy_setopt(m_pCurl, CURLOPT_READFUNCTION, HttpClientOnReadCallback); // 读回调:如Get
        curl_easy_setopt(m_pCurl, CURLOPT_READDATA, this);
        curl_easy_setopt(m_pCurl, CURLOPT_MAXREDIRS, 10L);
        curl_easy_setopt(m_pCurl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(m_pCurl, CURLOPT_NOPROGRESS, 0L);
        curl_easy_setopt(m_pCurl, CURLOPT_DNS_USE_GLOBAL_CACHE, 0L);
        curl_easy_setopt(m_pCurl, CURLOPT_PROGRESSDATA, this);
        curl_easy_setopt(m_pCurl, CURLOPT_PROGRESSFUNCTION, HttpClientOnProgressCallback);
        curl_easy_setopt(m_pCurl, CURLOPT_FAILONERROR, 1L);

        m_pHeadList = curl_slist_append(m_pHeadList, "Expect:");
        if (!headers.empty()) {
            for (const auto& it : headers) {
                auto tmp = it.first + ": " + it.second;
                m_pHeadList = curl_slist_append(m_pHeadList, tmp.c_str());
            }
        }

        curl_easy_setopt(m_pCurl, CURLOPT_HTTPHEADER, m_pHeadList);
        return 0;
    }
  • 上传文件核心参数
    int HttpClient::OnProgressCallback(double dt, double dn, double ult, double uln) {
        if (m_isExit != nullptr && m_isExit()) {
            return -1;
        }
        return 0;
    }
    
    // 读回调
    size_t HttpClient::OnReadCallback(void *buffer, size_t size, size_t nmemb, void *arg){
        return nmemb*size;
    }

    // 写回调
    size_t HttpClient::OnWriteCallback(void *buffer, size_t size, size_t nmemb, void *arg) {
        if (buffer != nullptr && size > 0 && nmemb > 0) {
            m_result.append((char*)buffer, size*nmemb);
        }
        return size*nmemb;
    }

    // 获取必要的参数 具体看需求
    void HttpClient::UpdateInfo() {
        if (m_pCurl == nullptr) {
            return;
        }
        double dnsTime = 0;
        if (curl_easy_getinfo(m_pCurl, CURLINFO_NAMELOOKUP_TIME, &dnsTime) == CURLE_OK) {
            m_iDnsTime = dnsTime*1000;
        }
        long code = 0;
        if (curl_easy_getinfo(m_pCurl, CURLINFO_RESPONSE_CODE, &code) == CURLE_OK) {
            m_iCurlCode = code;
        }
        char *ip = nullptr;
        if ((curl_easy_getinfo(m_pCurl, CURLINFO_PRIMARY_IP, &ip) == CURLE_OK) && (ip != nullptr)) {
            m_remoteIp = ip;
        }
        double temp = 0.0;
        const unsigned int k_dwThousand = 1000;
        if (curl_easy_getinfo(m_pCurl, CURLINFO_TOTAL_TIME, &temp) == CURLE_OK) {
            m_interactionTime.dwTotalTime = (uint32_t)(temp * k_dwThousand);
        }
        if (curl_easy_getinfo(m_pCurl, CURLINFO_NAMELOOKUP_TIME, &temp) == CURLE_OK) {
            m_interactionTime.dwNameLookupTime = (uint32_t)(temp * k_dwThousand);
        }
        if (curl_easy_getinfo(m_pCurl, CURLINFO_CONNECT_TIME, &temp) == CURLE_OK) {
            m_interactionTime.dwConnectTime = (uint32_t)(temp * k_dwThousand);
        }
        if (curl_easy_getinfo(m_pCurl, CURLINFO_PRETRANSFER_TIME, &temp) == CURLE_OK) {
            m_interactionTime.dwPretransferTime = (uint32_t)(temp * k_dwThousand);
        }
        if (curl_easy_getinfo(m_pCurl, CURLINFO_FILETIME, &m_interactionTime.lFileTime) == CURLE_OK) {
            
        }
        if (curl_easy_getinfo(m_pCurl, CURLINFO_STARTTRANSFER_TIME, &temp) == CURLE_OK) {
            m_interactionTime.dwStartTransferTime = (uint32_t)(temp * k_dwThousand);
        }
        if (curl_easy_getinfo(m_pCurl, CURLINFO_REDIRECT_TIME, &temp) == CURLE_OK) {
            m_interactionTime.dwRedirectTime = (uint32_t)(temp * k_dwThousand);
        }
        if (curl_easy_getinfo(m_pCurl, CURLINFO_APPCONNECT_TIME, &temp) == CURLE_OK) {
            m_interactionTime.dwAppconnetTime = (uint32_t)(temp * k_dwThousand);
        }
    }

    // Get操作
    int HttpClient::GetData(std::string &result, const HttpClientUrlArgs &urlArgs) {
        if (initCurl(urlArgs) != 0) {
            xerror2(TSF"init curl error!");
            return -1;
        }

        m_result.clear();
        auto res = curl_easy_perform(m_pCurl);
        if (res != CURLE_OK) {
            xerror2(TSF"url: %_, res: %_", m_requestUrl.c_str(), res);
        }

        result.clear();
        result = std::move(m_result);
        UpdateInfo();
        return res;
    }

    // Post操作
    int HttpClient::PostData(std::string &result, const HttpClientUrlArgs &urlArgs, const std::string &content) {
        result.clear();
        if (initCurl(urlArgs) != 0) {
            xerror2(TSF"init curl error!");
            return -1;
        }
        curl_easy_setopt(m_pCurl, CURLOPT_POST, true);
        curl_easy_setopt(m_pCurl, CURLOPT_POSTFIELDS, content.c_str());
        m_result.clear();
        auto res = curl_easy_perform(m_pCurl);
        if (res != CURLE_OK) {
            xerror2(TSF"url: %_, res: %_", m_requestUrl.c_str(), res);
        }
        result = std::move(m_result);
        UpdateInfo();
        return res;
    }

    // 上传文件操作
    int HttpClient::PostBinaryData(std::string &result, const HttpClientUrlArgs &urlArgs, const std::string &filePath, const std::string &fileName) {
                if (initCurl(urlArgs) != 0) {
            xerror2(TSF"init curl error!");
            return -1;
        }

        if (m_pFormpost != nullptr) {
            curl_formfree(m_pFormpost);
            m_pFormpost = nullptr;
        }

        struct curl_httppost *lastPtr = nullptr;
        // 注意参数顺序, 这块踩过坑; 之前因为参数顺序问题查了好久
        curl_formadd(&m_pFormpost, &lastPtr, CURLFORM_PTRNAME, "log_file", CURLFORM_FILENAME, fileName.c_str(), CURLFORM_FILE, filePath.c_str(), CURLFORM_END);
        
        // 跟服务端定 需要什么字段, 就如下写法加什么字段
        curl_formadd(&m_pFormpost, &lastPtr, CURLFORM_PTRNAME, "client_id", CURLFORM_PTRCONTENTS, XXX.c_str(), CURLFORM_END);
        curl_easy_setopt(m_pCurl, CURLOPT_HTTPPOST, m_pFormpost);


        m_result.clear();
        auto res = curl_easy_perform(m_pCurl);
        if (res != CURLE_OK) {
            xerror2(TSF"curl easy perform error, url: %_, res: %_", m_requestUrl.c_str(), res);
            return res;
        }

        // 获取WriteCallBack中的结果信息来判断这次执行是否成功以及查询出错原因
        result = std::move(m_result);
        UpdateInfo();
        return 0;
    }
  • 外部调用核心参数
    // 上传文件部分调用核心代码
    HttpClientUrlArgs urlArgs;
    ...
    // Content-Type格式——上传文件
    urlArgs.headers["Content-Type"] = "multipart/form-data";

    // Content-Type格式——Post字符串
    urlArgs.headers["Content-Type"] = "text/plain"; // 一般是内存中字符串, 如日志上传
    urlArgs.headers["Content-Type"] = "application/json"; // 默认的json格式

    // Content-Type格式——Get操作
    urlArgs.headers["Content-Type"] = 可以不指定这个字段, 走默认"application/json";
    
    // 注意: Curl提供了客户线程退出标识检查机制(新版本废弃)
    int HttpClient::OnProgressCallback(double dt, double dn, double ult, double uln) {
        if (m_isExit != nullptr && m_isExit()) {
            return -1;
        }
        return 0;
    }
    
    // 如下设置回调isExit标识当前线程是否退出
    ...
    std::shared_ptr<HttpClient> httpClient = std::make_shared<HttpClient>(XXX::bind(&XXX::isExit, this));
    ...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ปรัชญา แค้วคำมูล

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值