分享个C++封装Libcurl代码(支持下载文件、GET\POST、重定向断点续传等功能)
2016-03-01 20:07
841 查看
前言
前面分享过一个Windows上封装Winhttp和WinInet API的代码,结果下载页好评特别多(呵呵),谢谢大家赏脸。文章地址:开源一个C++实现的简单HTTP协议处理库,里面有代码资源下载地址。但是,在实际开发过程中我发现WinHttpAPI严重依赖微软的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,在虚函数中实现下就可以了。
相关文章推荐
- C语言实现单链表的逆序打印(带头结点)
- c++中string的用法积累
- C++面向对象编程分享----20160301_李楚煌
- C++指针的一些小总结
- Codeforces Round #338 (Div. 2) A. Bulbs (python and c++)
- C++ code 读取某给定路径下的当前文件夹名
- Effective C++ 读书笔记 条款1~2
- C语言实现单链表的节点插入(带头结点)
- 第三届蓝桥杯C++B组省赛
- 第四届蓝桥杯C++B组省赛
- 第五届蓝桥杯C++B组省赛
- 第六届蓝桥杯C++B组省赛
- C++::My Effective C++ (二)
- C++ virtual function (not pure virtual function)
- C++中常见问题处理
- 【POJ 3691】【hdu 2457】DNA repair 中文题意&题解&代码(C++)
- C++虚析构函数、纯虚析构函数
- C++中虚析构函数的作用
- C语言变量类型与内存
- c语言:调用函数打印How do you do!