使用CURL读取HTTP数据到字符串或者文件中

时间:2023-01-04 14:41:42

这两天简单研究了下CURL访问网站读取数据的方式,做了简单的实验,思路很简单,就是提供一个URL,然后从URL返回的数据读取出来,显示到控制台,或者存储到文件中。具体的描述以及注意事项,代码的注释中都有写明。

上代码。

// DemoLibcurl.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#define CURL_STATICLIB            // 由于我使用的是CURL静态链接,所以需要定义上这个宏
#include "http://www.cnblogs.com/../include/curl/curl.h"

// 一下三个lib为libcurl.lib所依赖的三个库
#pragma comment(lib, "Wldap32.lib")
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Advapi32.lib")

#ifdef _DEBUG
#pragma comment(lib,"http://www.cnblogs.com/../lib/libcurld.lib")
#else
#pragma comment(lib,"http://www.cnblogs.com/../lib/libcurl.lib")
#endif

#include <iostream>
#include <string>
using namespace std;


// 定义一个break宏
#define BREAK_ON_NOT_EQUAL(compareValue, resultValue) {if((compareValue) != (resultValue))break;}


// 执行输出到标准输出流
bool PerformToStdout(IN CURL* nphEasycurl);

// curl指定格式的回调函数
size_t WriteToString(void *buffer, size_t size, size_t nmemb, void *userp);

size_t WriteToFile(void *buffer, size_t size, size_t nmemb, void *userp);

// 执行输出到字符串
bool PerformToString(IN CURL* nphEasycurl, IN string& rstrBuffer);

// 执行输出到文件
bool PerformToFile(IN CURL* nphEasycurl, IN char* nscFileName, bool nbIsUseCallBackMethod = false);

int _tmain(int argc, _TCHAR* argv[])
{
    // 进行初始化
    CURL* lphEasycurl = curl_easy_init(); 
    do 
    {
        // 初始化失败
        if(NULL == lphEasycurl)
        {
            cout << "Error: Init curl handle failed." << endl;
            break;
        }

        // 获取当前使用的curl库的版本信息
        curl_version_info_data* ldVersionInfo = curl_version_info(CURLVERSION_NOW);
        if(NULL != ldVersionInfo && ldVersionInfo->age > 0)
        {
            //// 判断是否支持所需要的功能
            //if(1 !=  (ldVersionInfo->features & CURL_VERSION_GSSNEGOTIATE))        // 不明白这里要用那个宏去进行判断才表示支持HTTP
            //{
            //    cout << "Error: Current Curl version do`nt support HTTP." << endl;
            //    break;
            //}
        }
        else        // 获取版本信息失败
        {
            cout << "Error: Get version info of Curl failed." << endl;
            break;
        }

        // 设置接受错误信息的缓存,CURL_ERROR_SIZE为curl要求的最小大小
        char lscErrBuffer[CURL_ERROR_SIZE];            
        curl_easy_setopt(lphEasycurl, CURLOPT_ERRORBUFFER, &lscErrBuffer);            // 接受错误描述

        // 设置URL
        curl_easy_setopt(lphEasycurl, CURLOPT_URL, "http://www.cnn.com/"); 

        // 执行
        int liOutputType = 2;                // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~通过更改这个值,来尝试不同的方式
        bool lbIsPerformSuccess = false;
        string lstrDataBuffer;
        switch(liOutputType)
        {
        case 1:                // 输出到字符串
            {
                lbIsPerformSuccess = PerformToString(lphEasycurl, lstrDataBuffer);
            }
            break;

        case 2:                // 输出到文件
            {
                lbIsPerformSuccess = PerformToFile(lphEasycurl, "output.txt", true);        // ~~~~~~~~~更改第三个参数来调用不同的实现方式
            }
            break;

        default:            // 输出到标准输出
            {
                lbIsPerformSuccess = PerformToStdout(lphEasycurl);
            }
            break;
        }
        
        // 执行失败,输出错误描述
        if(false == lbIsPerformSuccess)
        {
            cout << "Error: " << lscErrBuffer << endl;
            break;
        }

        // 根据设置的类型进行输出
        if(1 == liOutputType)
        {
            cout  << "The data read from url:" << lstrDataBuffer.c_str() << endl;
        }
        else if(2 == liOutputType)
        {
            cout << "Success: The data have saved to output.txt" << endl;
        }


    } while (false);

    // 清空初始化数据
    if(NULL != lphEasycurl)
        curl_easy_cleanup(lphEasycurl); 

    cout<<endl<<"Press anykey to continue!"<<endl;
    getchar();
    return 0;
}





bool PerformToStdout(IN CURL* nphEasycurl)
{
    bool lbResult = false;
    do 
    {

        if(CURLE_OK != curl_easy_perform(nphEasycurl))
            break;

        lbResult = true;
    } while (false);

    return lbResult;
}




// 功能:
//        libcurl写数据所使用的标准回调函数格式
// 参数:
//        buffer所指向数据块的字节数为size * nmemb,注意不会以0结尾。
//        userp是通过设置CURLOPT_WRITEDATA属性传进来的指针
// 返回值:
//        通常作为实际处理的字节数,如果处理的总字节数和传入的字节总数不同,curl将中止操作并返回错误码CURLE_WRITE_ERROR
size_t WriteToString(void *buffer, size_t size, size_t nmemb, void *userp)
{
    *((std::string*)userp) += (char*)buffer;        // 不是说不是以0结尾的吗?这里用这种方式不会访问越界么?

    return size * nmemb;
}

bool PerformToString(IN CURL* nphEasycurl, IN string& rstrBuffer)
{
    bool lbResult = false;
    do 
    {
        curl_easy_setopt(nphEasycurl, CURLOPT_WRITEFUNCTION, WriteToString);    // 如果不提供这个回调函数,curl将会将数据输出到stdout中。
        curl_easy_setopt(nphEasycurl, CURLOPT_WRITEDATA, &rstrBuffer);
        
        if(CURLE_OK != curl_easy_perform(nphEasycurl))
            break;

        lbResult = true;
    } while (false);

    return lbResult;
}

// 同WriteToString
size_t WriteToFile(void *buffer, size_t size, size_t nmemb, void *userp)
{
    FILE* lphFile = (FILE*)userp;
    if(NULL != lphFile)
    {
        fwrite(buffer, size, nmemb, lphFile);
    }

    return size * nmemb;
}

bool PerformToFile(IN CURL* nphEasycurl, IN char* nscFileName, bool nbIsUseCallBackMethod)
{
    bool lbResult = false;
    FILE* lphFile = NULL;                // CURL要求必须使用FILE
    do 
    {
        if(NULL == nscFileName)
            break;

        // 打开文件
        lphFile = fopen(nscFileName, "wb+");
        if(NULL == lphFile)
        {
            cout << "file open failed." << endl;
            break;
        }
        
        if(false != nbIsUseCallBackMethod)            // 使用回调方式
        {
            // 方式1,将File*传递给回调函数,自己进行处理。
            BREAK_ON_NOT_EQUAL(CURLE_OK, curl_easy_setopt(nphEasycurl, CURLOPT_WRITEFUNCTION, WriteToFile));
        }

        // 方式2,直接将FILE*传递给CURL去进行处理,!注意,CURL默认会使用fwrite写入数据,这也是为什么并且只能传入FILE*的原因。
        BREAK_ON_NOT_EQUAL(CURLE_OK, curl_easy_setopt(nphEasycurl, CURLOPT_WRITEDATA, lphFile));

        // 方式3,用PerformToFile,然后将返回的数据写入File中,这里不进行实验

        // 执行
        BREAK_ON_NOT_EQUAL(CURLE_OK, curl_easy_perform(nphEasycurl));

        lbResult = true;
    } while (false);

    if(NULL != lphFile)
        fclose(lphFile);

    return lbResult;
}