GET

::curl_easy_setopt(curl_, CURLOPT_URL, final_url.c_str());
::curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1);
::curl_easy_setopt(curl_, CURLOPT_HEADER, with_header);
if (!vHeader.empty())
{
struct curl_slist* headers = NULL;
for (int i = 0; i < vHeader.size();++i)
{
headers = curl_slist_append(headers, vHeader[i].c_str());
}
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
}
::curl_easy_setopt(curl_, CURLOPT_TIMEOUT, timeout);
::curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback4Http);
::curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void*) &rsp);
::curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1);
::curl_easy_perform(curl_);
//暂时不考虑重定向

POST

::curl_easy_reset(curl_);

::curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
::curl_easy_setopt(curl_, CURLOPT_POST, 1);
::curl_easy_setopt(curl_, CURLOPT_HEADER, with_header);
//构建HTTP报文头
if (!vHeader.empty())
{
struct curl_slist* headers = NULL;
for (int i = 0; i < vHeader.size();++i)
{
headers = curl_slist_append(headers, vHeader[i].c_str());
}
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
}
::curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, data);
::curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, data_len);
::curl_easy_setopt(curl_, CURLOPT_TIMEOUT, timeout);
::curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback4Http);
::curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void*) &rsp);
::curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
::curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L);
::curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1);
::curl_easy_perform(curl_);
//暂时不考虑重定向

HEAD

::curl_easy_setopt(curl_,CURLOPT_NOBODY,1L);

DOWNLOAD

::curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
::curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L);
::curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, 300L);
::curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_TIME, 1800L);
::curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_LIMIT, 10L);
::curl_easy_setopt(curl_, CURLOPT_USERAGENT, CURL_USER_AGENT);
::curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
::curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L);
::curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);

//写回调
::curl_easy_setopt(curl_, CURLOPT_HEADERFUNCTION, HeaderCallback4Download);
::curl_easy_setopt(curl_, CURLOPT_HEADERDATA, &dld_ctx);
::curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback4Download);
::curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &dld_ctx);
//设置进度
::curl_easy_setopt(curl_, CURLOPT_NOPROGRESS, 0L);
::curl_easy_setopt(curl_, CURLOPT_PROGRESSFUNCTION, ProgressCallback4Download);
::curl_easy_setopt(curl_, CURLOPT_PROGRESSDATA, &dld_ctx);

::curl_easy_perform(curl_);

//暂时不考虑重定向

下面是完整源码:
​​CurlClient.h

#pragma once
#include <vector>
#include <string>
#include "curl.h"
#include "easy.h"


enum ESrcEncode{ EnUrlCode, EnUtf8Code };//默认EnUrlCode
#define SAFE_FREE(p) if(p != NULL){free(p);p=NULL;}
struct CurlChunk
{
char* buf;
size_t size;

CurlChunk()
{
buf = NULL;
size = 0;
}

~CurlChunk()
{
SAFE_FREE(buf);
}
};

// 下载回调函数,返回0继续下载,否则停止下载
typedef int (*CurlDownloadSink)(const std::string& file_name,
double dltotal,
double dlnow,
void* userp);

class CurlClient
{

public:
CurlClient();
~CurlClient();

public:
static int GlobalInit();
static void GlobalCleanup();
static const char* GetErrMsg(CURLcode code);

CURLcode DoHttpGet(CurlChunk& rsp,
const std::string& url,
const std::string& data,
long timeout = 10,
long with_header = 0);

CURLcode DoHttpPost(CurlChunk& rsp,
const std::string& url,
const char* data,
const int data_len,
long timeout = 10,
long with_header = 0,
std::vector<std::string> vHeader = std::vector<std::string>());

CURLcode DownloadFile(const std::string& url,
const std::string& save_dir,
std::string& file_name,
CurlDownloadSink sink = NULL,
void* userp = NULL,
std::string user_agent = "",
ESrcEncode srcEncode = EnUrlCode,
bool bHead = false,
bool bResetLevel = true);

private:
static size_t WriteCallback4Http(void* contents,
size_t size,
size_t nmemb,
void* userp);

static size_t HeaderCallback4Download(char *ptr,
size_t size,
size_t nmemb,
void *userdata);

static size_t WriteCallback4Download(char *ptr,
size_t size,
size_t nmemb,
void *userdata);

static int ProgressCallback4Download(void *userdata,
double dltotal,
double dlnow,
double ultotal,
double ulnow);

private:
CURL* curl_;
int m_nRecurLevel;//重定次数
};

CurlClient.cpp

#include <fstream>
#include "CurlClient.h"
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")

#pragma warning(disable: 4996) // 'function': was declared deprecated

#define CURL_USER_AGENT "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"


struct CurlDownloadContext
{
CURL* curl;
double total_time;
CurlDownloadSink sink;
void* userp;
std::string save_dir;
std::string file_name;
UINT64 file_size;
FILE* file;
std::string redirect_url;
ESrcEncode eSrcEncode;//下载文件名的编码方式
CurlDownloadContext()
{
file_size = 0;
curl = NULL;
total_time = 0;
sink = NULL;
userp = NULL;
file = NULL;
eSrcEncode = EnUrlCode;
}

~CurlDownloadContext()
{
if (file != NULL)
{
fclose(file);
}
}
};

int CurlClient::GlobalInit()
{
CURLcode ret = ::curl_global_init(CURL_GLOBAL_ALL);
if (ret != CURLE_OK)
{
return -1;
}
return 0;
}

void CurlClient::GlobalCleanup()
{
::curl_global_cleanup();
}

const char* CurlClient::GetErrMsg(CURLcode code)
{
return ::curl_easy_strerror(code);
}

CurlClient::CurlClient()
{
curl_ = ::curl_easy_init();
m_nRecurLevel = 0;
}

CurlClient::~CurlClient()
{
if (curl_ != NULL)
{
::curl_easy_cleanup(curl_);
}
}

CURLcode CurlClient::DoHttpGet(CurlChunk& rsp,
const std::string& url,
const std::string& data,
long timeout,
long with_header)
{
if (curl_ == NULL || url.empty())
{
return CURLE_BAD_FUNCTION_ARGUMENT;
}

std::string final_url(url);
if (!data.empty())
{
final_url.append("?");
final_url.append(data);
}

::curl_easy_reset(curl_);

::curl_easy_setopt(curl_, CURLOPT_URL, final_url.c_str());
::curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1);
::curl_easy_setopt(curl_, CURLOPT_HEADER, with_header);
::curl_easy_setopt(curl_, CURLOPT_TIMEOUT, timeout);
::curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback4Http);
::curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void*) &rsp);
::curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1);

return ::curl_easy_perform(curl_);
}

CURLcode CurlClient::DoHttpPost(CurlChunk& rsp,
const std::string& url,
const char* data,
const int data_len,
long timeout,
long with_header,
std::vector<std::string> vHeader)
{
if (curl_ == NULL || url.empty() || data == NULL || data_len == 0)
{
return CURLE_BAD_FUNCTION_ARGUMENT;
}

::curl_easy_reset(curl_);

::curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
::curl_easy_setopt(curl_, CURLOPT_POST, 1);
::curl_easy_setopt(curl_, CURLOPT_HEADER, with_header);
//构建HTTP报文头
if (!vHeader.empty())
{
struct curl_slist* headers = NULL;
for (int i = 0; i < vHeader.size();++i)
{
headers = curl_slist_append(headers, vHeader[i].c_str());
}
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
}
::curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, data);
::curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, data_len);
::curl_easy_setopt(curl_, CURLOPT_TIMEOUT, timeout);
::curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback4Http);
::curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void*) &rsp);
::curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
::curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L);
::curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1);


return ::curl_easy_perform(curl_);
}

size_t CurlClient::WriteCallback4Http(void* contents,
size_t size,
size_t nmemb,
void* userp)
{
const size_t real_size = size * nmemb;
struct CurlChunk* chunk = (struct CurlChunk*) userp;

chunk->buf = (char*) realloc(chunk->buf, chunk->size + real_size + 1);
if (chunk->buf == NULL)
{
return 0;
}

memcpy(&(chunk->buf[chunk->size]), contents, real_size);
chunk->size += real_size;
chunk->buf[chunk->size] = '\0';

return real_size;
}

bool ParseCurlLink(const std::string& url, std::string& file_name)
{
size_t pos = url.find("://");
size_t end_pos = std::string::npos;
if (pos == std::string::npos)
return false;

std::string url_name = url.substr(pos + strlen("://"));
//StringUtil::rtrim(StringUtil::trim(url_name), "/");

pos = url_name.rfind('/');
if (pos == std::string::npos)
file_name.assign("index.html");
else
{
end_pos = url_name.find_first_of("?", pos + 1);
if (end_pos != std::string::npos)
file_name = url_name.substr(pos + 1, end_pos - pos - 1);
else
file_name = url_name.substr(pos + 1);
}


return true;
}

CURLcode CurlClient::DownloadFile(const std::string& url,
const std::string& save_dir,
std::string& file_name,
CurlDownloadSink sink,
void* userp,
std::string user_agent,
ESrcEncode srcEncode,
bool bHead,
bool bResetLevel)
{
if (bResetLevel)
{
m_nRecurLevel = 0;
}
++m_nRecurLevel;
//递归深度默认为三次
if (curl_ == NULL || url.empty() || save_dir.empty() || m_nRecurLevel > 3)
{
return CURLE_BAD_FUNCTION_ARGUMENT;
}

CurlDownloadContext dld_ctx;
dld_ctx.curl = curl_;
dld_ctx.sink = sink;
dld_ctx.userp = userp;
dld_ctx.eSrcEncode = srcEncode;
dld_ctx.save_dir = save_dir;
dld_ctx.file_name = file_name;

if (save_dir[save_dir.length() - 1] != '\\')
{
dld_ctx.save_dir.append("\\");
}

if (dld_ctx.file_name.empty() && !ParseCurlLink(url, dld_ctx.file_name))
{
return CURLE_URL_MALFORMAT;
}

//防止curl重定向导致的名称不对
if(file_name.empty() ||file_name == "")
file_name = dld_ctx.file_name;

::curl_easy_reset(curl_);

::curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
::curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L);
::curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, 300L);
::curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_TIME, 1800L);
::curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_LIMIT, 10L);
::curl_easy_setopt(curl_, CURLOPT_USERAGENT, CURL_USER_AGENT);
::curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
::curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L);
::curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);
if(bHead)
::curl_easy_setopt(curl_,CURLOPT_NOBODY,1L);
//构建HTTP报文头
if(user_agent != "")
{
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, user_agent.c_str());
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
}
::curl_easy_setopt(curl_, CURLOPT_HEADERFUNCTION, HeaderCallback4Download);
::curl_easy_setopt(curl_, CURLOPT_HEADERDATA, &dld_ctx);
::curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback4Download);
::curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &dld_ctx);

if (!bHead && sink != NULL)
{
::curl_easy_setopt(curl_, CURLOPT_NOPROGRESS, 0L);
::curl_easy_setopt(curl_, CURLOPT_PROGRESSFUNCTION, ProgressCallback4Download);
::curl_easy_setopt(curl_, CURLOPT_PROGRESSDATA, &dld_ctx);
}

CURLcode ret = ::curl_easy_perform(curl_);
if (ret == CURLE_OK)
{
if (!dld_ctx.redirect_url.empty() &&
dld_ctx.redirect_url != url)
{
return DownloadFile(dld_ctx.redirect_url, save_dir, file_name, sink, userp,user_agent,srcEncode,bHead,false);
}

file_name = dld_ctx.file_name;
if (!bHead && dld_ctx.file == NULL)
{
std::string file_path = dld_ctx.save_dir + dld_ctx.file_name;
std::ofstream fout(file_path);
fout.close();
}
}

//通过返回码判断文件是否下载成功
long retcode = 0;
CURLcode code = curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &retcode);
if ((code == CURLE_OK) && retcode < 400)
{
return CURLE_OK;
}
return CURLE_BAD_FUNCTION_ARGUMENT;
}

size_t CurlClient::HeaderCallback4Download(char *ptr,
size_t size,
size_t nmemb,
void *userdata)
{
CurlDownloadContext* dld_ctx = (CurlDownloadContext*)userdata;
const char key_loc[] = "Location:";
const char key_disp[] = "Content-Disposition:";
const char key_fn[] = "filename=";
const char key_length[] = "Content-Length:";

if (_strnicmp(ptr, key_loc, strlen(key_loc)) == 0)
{
dld_ctx->redirect_url.assign(ptr + strlen(key_loc));
//去掉\r\n
if (dld_ctx->redirect_url.size() > 2)
{
dld_ctx->redirect_url.resize(dld_ctx->redirect_url.size() - 2);
}
}
else if(_strnicmp(ptr, key_length, strlen(key_length)) == 0)
{
std::string file_length(ptr + strlen(key_length));
dld_ctx->file_size = _atoi64(file_length.c_str());
}
else if (_strnicmp(ptr, key_disp, strlen(key_disp)) == 0)
{
char* fn = StrStrIA(ptr, key_fn);
if (fn != NULL)
{
fn += strlen(key_fn);
char* sep = strchr(fn, ';');
dld_ctx->file_name.assign(fn, (sep == NULL ? strlen(fn) : sep - fn));
//if(dld_ctx->eSrcEncode == EnUtf8Code)
//StringUtil::Utf8ToAnsi(std::string(dld_ctx->file_name),dld_ctx->file_name);
//else
//dld_ctx->file_name = StringUtil::UrlDecode(dld_ctx->file_name);

//StringUtil::trim(StringUtil::trim(dld_ctx->file_name), "\"");
}
}

return size * nmemb;
}

size_t CurlClient::WriteCallback4Download(char *ptr,
size_t size,
size_t nmemb,
void *userdata)
{
CurlDownloadContext* dld_ctx = (CurlDownloadContext*)userdata;

if (!dld_ctx->redirect_url.empty())
{
return size * nmemb;
}

if (dld_ctx->file == NULL)
{
std::string file_path = dld_ctx->save_dir + dld_ctx->file_name;
dld_ctx->file = fopen(file_path.c_str(), "wb");
if (dld_ctx->file == NULL)
{
return 0;
}
}

return fwrite(ptr, size, nmemb, dld_ctx->file);
}

int CurlClient::ProgressCallback4Download(void *userdata,
double dltotal,
double dlnow,
double ultotal,
double ulnow)
{
CurlDownloadContext* dld_ctx = (CurlDownloadContext*)userdata;

if (dld_ctx->file_name.empty() || !dld_ctx->redirect_url.empty())
{
return 0;
}

double total_time = 0;
::curl_easy_getinfo(dld_ctx->curl, CURLINFO_TOTAL_TIME, &total_time);

if (dld_ctx->total_time == 0 || total_time - dld_ctx->total_time > 1)
{
dld_ctx->total_time = total_time;
if (dld_ctx->sink(dld_ctx->file_name, dltotal, dlnow, dld_ctx->userp) != 0)
{
return 1;
}
}

return 0;
}