分享个C++封装Libcurl代码(支持下载文件、GET\POST、重定向断点续传等功能)

前言

前面分享过一个Windows上封装Winhttp和WinInet API的代码,结果下载页好评特别多(呵呵),谢谢大家赏脸。文章地址:开源一个C++实现的简单HTTP协议处理库,里面有代码资源下载地址。但是,在实际开发过程中我发现WinHttp API严重依赖微软的IE组件,下载过程中会出现些很少见的异常。(比如下载文件和使用chrome浏览器下载的文件不一样。。。。)因此,有必要替换掉这个HTTP封装库。为什么选择Libcurl?大名鼎鼎的Libcurl,很多开源软件以及国内客户端都使用了,跨平台、强大的功能(各种请求、重定向、断电续传、支持HTTPS……)。前面还写过一篇使用Libcurl下载文件的小例子,那只是很久前的入门文章:使用libcurl下载文件小例

代码

/*****************************************
*封装Libcurl下载库
*author:Jelin
*date:2016年2月24日		
*/
#pragma once
#include <curl/curl.h>
#include <string>
using std::string;





class CLibcurlCallback
{
public:
	virtual void Progress(void* lpParam, double dTotal, double bLoaded) = 0;
};

enum LibcurlFlag
{
	Lf_None = 0,
	Lf_Download,
	Lf_Post,
	Lf_Get,
};

class CLibcurl
{
public:
	CLibcurl(void);
	~CLibcurl(void);
	/******************************************************************************
	*封装类的外部调用接口
	*/
	bool SetPort(LONG port);											//设置连接端口号
	bool SetTimeout(int nSecond);										//设置执行超时(秒)
	bool SetConnectTimeout(int nSecond);								//设置连接超时(秒)
	bool SetUserAgent(LPCSTR lpAgent);									//设置用户代理
	bool SetResumeFrom(LONG lPos);										//设置断点续传起始位置
	bool SetResumeFromLarge(LONGLONG llPos);							//设置断点续传起始位置,针对大文件
	bool AddHeader(LPCSTR lpKey, LPCSTR lpValue);						//添加自定义头
	void ClearHeaderList();												//清理HTTP列表头
	bool SetCookie(LPCSTR lpCookie);									//设置HTTP请求cookie
	bool SetCookieFile(LPCSTR lpFilePath);								//设置HTTP请求cookie文件
	const char* GetError()const;										//获取错误详细信息
	void SetCallback(CLibcurlCallback* pCallback, void* lpParam);		//设置下载进度回调
	bool DownloadToFile(LPCSTR lpUrl, LPCSTR lpFile);					//下载文件到磁盘
	bool Post(LPCSTR lpUrl, LPCSTR lpData);								//Post 字符串数据
	bool Post(LPCSTR lpUrl, unsigned char* lpData, unsigned int nSize); //Post 字符串或者二进制数据
	bool Get(LPCSTR lpUrl);												//Get 请求
	const string& GetRespons()const;									//获取Post/Get请求返回数据
	const char*	GetResponsPtr()const;									//获取Post/Get请求返回数据

protected:
	static size_t	WriteCallback(void* pBuffer, size_t nSize, size_t nMemByte, void* pParam);
	static size_t	HeaderCallback(void* pBuffer, size_t nSize, size_t nMemByte, void* pParam);
	static int		ProgressCallback(void *pParam, double dltotal, double dlnow, double ultotal, double ulnow);

private:
	CURL	*m_pCurl;
	LONG	m_nPort;
	HANDLE	m_hFile;
	CURLcode m_curlCode;
	string	m_strRespons;
	LibcurlFlag m_lfFlag;
	curl_slist *m_curlList;
	void	*m_pCallbackParam;
	CLibcurlCallback	*m_pCallback;
};

#include "StdAfx.h"
#include "Libcurl.h"
#include <assert.h>

#ifdef _DEBUG
#pragma comment(lib, "libcurl/libcurld_SSL")
#pragma comment(lib, "libcurl/libeay32.lib")
#pragma comment(lib, "libcurl/ssleay32.lib")
#else
#pragma comment(lib, "libcurl//libcurl")
#endif
#pragma comment(lib, "ws2_32")
#pragma comment(lib, "Iphlpapi")
#pragma comment(lib, "Wldap32")


CLibcurl::CLibcurl(void)
	: m_pCurl(NULL)
	, m_nPort(80)
	, m_hFile(INVALID_HANDLE_VALUE)
	, m_pCallback(NULL)
	, m_pCallbackParam(NULL)
	, m_curlCode(CURLE_OK)
	, m_lfFlag(Lf_None)
	, m_curlList(NULL)
{
	m_pCurl = curl_easy_init();
	curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, WriteCallback);
	curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, this);
}


CLibcurl::~CLibcurl(void)
{
	ClearHeaderList();
	curl_easy_cleanup(m_pCurl);
	if ( m_hFile != INVALID_HANDLE_VALUE )
	{
		CloseHandle(m_hFile);
	}
}

bool CLibcurl::SetPort( LONG port )
{
	if ( port == m_nPort ) 
		return true;
	m_nPort = port;
	m_curlCode = curl_easy_setopt(m_pCurl, CURLOPT_PORT, m_nPort);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::SetTimeout( int nSecond )
{
	if ( nSecond<0 )
		return false;
	m_curlCode = curl_easy_setopt(m_pCurl, CURLOPT_TIMEOUT, nSecond);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::SetConnectTimeout( int nSecond )
{
	if ( nSecond<0 )
		return false;
	m_curlCode = curl_easy_setopt(m_pCurl, CURLOPT_CONNECTTIMEOUT, nSecond);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::SetUserAgent( LPCSTR lpAgent )
{
	if ( NULL == lpAgent )
		return false;
	int nLen = strlen(lpAgent);
	if ( nLen == 0 )
		return false;
	m_curlCode = curl_easy_setopt(m_pCurl, CURLOPT_USERAGENT, lpAgent);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::SetResumeFrom( LONG lPos )
{
	if ( lPos<0 )
		return false;
	m_curlCode = curl_easy_setopt(m_pCurl, CURLOPT_RESUME_FROM, lPos);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::SetResumeFromLarge( LONGLONG llPos )
{
	if ( llPos<0 )
		return false;
	m_curlCode = curl_easy_setopt(m_pCurl, CURLOPT_RESUME_FROM_LARGE, llPos);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::AddHeader( LPCSTR lpKey, LPCSTR lpValue )
{
	assert(lpKey!=NULL && lpValue!=NULL);
	int nLen1 = strlen(lpKey), nLen2 = strlen(lpValue);
	assert(nLen1>0 && nLen2>0);
	string strHeader(lpKey);
	strHeader.append(": ");
	strHeader.append(lpValue);
	m_curlList = curl_slist_append(m_curlList, strHeader.c_str());
	m_curlCode = curl_easy_setopt(m_pCurl, CURLOPT_HTTPHEADER, m_curlList);
	return CURLE_OK == m_curlCode;
}

void CLibcurl::ClearHeaderList()
{
	if ( m_curlList )
	{
		curl_slist_free_all(m_curlList);
		m_curlList = NULL;
	}
}

bool CLibcurl::SetCookie( LPCSTR lpCookie )
{
	assert(lpCookie!=NULL);
	m_curlCode = curl_easy_setopt(m_pCurl, CURLOPT_COOKIE, lpCookie);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::SetCookieFile( LPCSTR lpFilePath )
{
	assert(lpFilePath!=NULL);
	m_curlCode = curl_easy_setopt(m_pCurl, CURLOPT_COOKIEFILE, lpFilePath);
	return CURLE_OK == m_curlCode;
}

const char* CLibcurl::GetError() const
{
	return curl_easy_strerror(m_curlCode);
}

void CLibcurl::SetCallback( CLibcurlCallback* pCallback, void* lpParam )
{
	m_pCallbackParam = lpParam;
	m_pCallback = pCallback;
}

bool CLibcurl::DownloadToFile( LPCSTR lpUrl, LPCSTR lpFile )
{
	CURLcode code = curl_easy_setopt(m_pCurl, CURLOPT_URL, lpUrl);
	DeleteFileA(lpFile);
	m_hFile = CreateFileA(lpFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if ( INVALID_HANDLE_VALUE == m_hFile )
	{
		return FALSE;
	}
	curl_easy_setopt(m_pCurl, CURLOPT_NOPROGRESS, 0);
	curl_easy_setopt(m_pCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
	curl_easy_setopt(m_pCurl, CURLOPT_PROGRESSDATA, this);
	m_lfFlag = Lf_Download;
	//开始执行请求
	m_curlCode = curl_easy_perform(m_pCurl);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::Post( LPCSTR lpUrl, LPCSTR lpData )
{
	assert(lpData!=NULL);
	curl_easy_setopt(m_pCurl, CURLOPT_POST, 1);
	curl_easy_setopt(m_pCurl, CURLOPT_POSTFIELDS, lpData);
	//curl_easy_setopt(m_pCurl, CURLOPT_POSTFIELDSIZE, lpData);
	curl_easy_setopt(m_pCurl, CURLOPT_URL, lpUrl);
	m_lfFlag = Lf_Post;
	m_strRespons.clear();
	m_curlCode = curl_easy_perform(m_pCurl);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::Post( LPCSTR lpUrl, unsigned char* lpData, unsigned int nSize )
{
	assert(lpData!=NULL && nSize>0);
	curl_easy_setopt(m_pCurl, CURLOPT_POST, 1);
	curl_easy_setopt(m_pCurl, CURLOPT_POSTFIELDS, lpData);
	curl_easy_setopt(m_pCurl, CURLOPT_POSTFIELDSIZE, nSize);
	curl_easy_setopt(m_pCurl, CURLOPT_URL, lpUrl);
	m_lfFlag = Lf_Post;
	m_strRespons.clear();
	m_curlCode = curl_easy_perform(m_pCurl);
	return CURLE_OK == m_curlCode;
}

bool CLibcurl::Get( LPCSTR lpUrl )
{
	assert(lpUrl!=NULL);
	curl_easy_setopt(m_pCurl, CURLOPT_HTTPGET, 1);
	curl_easy_setopt(m_pCurl, CURLOPT_URL, lpUrl);
	curl_easy_setopt(m_pCurl, CURLOPT_FOLLOWLOCATION, 1);//支持重定向
	curl_easy_setopt(m_pCurl, CURLOPT_SSL_VERIFYPEER, 0L);
	curl_easy_setopt(m_pCurl, CURLOPT_SSL_VERIFYHOST, 0L);
	m_lfFlag = Lf_Get;
	m_strRespons.clear();
	m_curlCode = curl_easy_perform(m_pCurl);
	return CURLE_OK == m_curlCode;
}

const string& CLibcurl::GetRespons() const
{
	return m_strRespons;
}

const char* CLibcurl::GetResponsPtr() const
{
	return m_strRespons.c_str();
}

size_t CLibcurl::WriteCallback( void* pBuffer, size_t nSize, size_t nMemByte, void* pParam )
{
	//把下载到的数据以追加的方式写入文件(一定要有a,否则前面写入的内容就会被覆盖了)
	CLibcurl* pThis = (CLibcurl*)pParam;
	DWORD dwWritten = 0;
	switch( pThis->m_lfFlag )
	{
	case Lf_Download://下载
		{
			if ( pThis->m_hFile == INVALID_HANDLE_VALUE )
				return 0;
			if ( !WriteFile(pThis->m_hFile, pBuffer, nSize*nMemByte, &dwWritten, NULL) )
				return 0;
		}
		break;
	case Lf_Post://Post数据
	case Lf_Get://Get数据
		{
			pThis->m_strRespons.append((const char*)pBuffer, nSize*nMemByte);
			dwWritten = nSize*nMemByte;
		}
		break;
	case Lf_None://未定义
		break;
	}
	return dwWritten;
}

size_t CLibcurl::HeaderCallback( void* pBuffer, size_t nSize, size_t nMemByte, void* pParam )
{
	CLibcurl* pThis = (CLibcurl*)pParam;
	return 0;
}

int CLibcurl::ProgressCallback( void *pParam, double dltotal, double dlnow, double ultotal, double ulnow )
{
	CLibcurl* pThis = (CLibcurl*)pParam;
	if ( pThis->m_pCallback )
	{
		pThis->m_pCallback->Progress(pThis->m_pCallbackParam, dltotal, dlnow);
	}
	return 0;
}
我对代码的风格比较敏感,写代码必须要整齐干净,容易阅读。(PS:我的注释也是很整齐的,到了这里都乱了,CSDN的网页编辑器!)


封装的目的是让Libcurl更容易使用,使用了C++包装了一系列C函数,方便我们直接设置一些属性,调用方法,获取返回数据和错误信息等。


使用

下面代码介绍了使用封装库进行下载、Get请求和Post请求

// LibcurlLoad.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "Libcurl.h"




class CLibcurlCallbackEx
	: public CLibcurlCallback
{
public:
	virtual void Progress(void* lpParam, double dTotal, double dLoaded)
	{
		if ( dTotal == 0.0 )
			 return ;
		double bPercent = (dLoaded/dTotal)*100;
		printf("下载进度:%0.2lf%%\n", bPercent);
	}

};

void DownloadTest();
void PostTest();
void GetTest();
int _tmain(int argc, _TCHAR* argv[])
{
	//DownloadTest();
	//PostTest();
	GetTest();
	return 0;
}

void DownloadTest()
{
	const char* pUrl = "http://download.*******.com/software/1.2.3.3639/32//Package";
	CLibcurl libcurl;
	CLibcurlCallbackEx cb;
	libcurl.SetUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36");
	libcurl.SetConnectTimeout(2);
	libcurl.SetResumeFrom(2);
	libcurl.SetCallback(&cb, NULL);
	libcurl.DownloadToFile(pUrl, "c:\\test.zip");
}

void PostTest()
{
	CLibcurl libcurl;
	libcurl.SetUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36");
	libcurl.SetPort(80);
	libcurl.SetConnectTimeout(2);
	libcurl.AddHeader("name", "Jelin");
	libcurl.AddHeader("sex", "man");
	libcurl.SetCookieFile("c:\\cookie");
	char* pData = "maintype=10001&subtype=100&appver=2.5.19.3753&sysver=Microsoft Windows 7&applist=100:15,200:2&sign=2553d888bc922275eca2fc539a5f0c1b";
	libcurl.Post("http://interface.***********.com/v2/stat/index/jingpin", pData);
	string strRet = libcurl.GetRespons();

}

void GetTest()
{
	CLibcurl libcurl;
	libcurl.SetUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36");
	libcurl.Get("https://www.baidu.com");
	const char* pHtml = libcurl.GetResponsPtr();
	const char* pError = libcurl.GetError();
}

如果你想知道下载进度,请继承CLibcurlCallback,在虚函数中实现下就可以了。

  • 6
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值