【C++开源库使用】使用libcurl开源库发送url请求(http请求)去下载用户头像文件(附完整源码)
目录
1、libcurl介绍
2、libcurl库源码下载与编译
3、调用libcurl库的API接口实现http/https请求发送,实现头像文件下载
4、发送图片url下载图片文件的完整代码展示
5、使用libcurl发送https请求时可能会遇到的两个错误
在某SDK项目中,第三方厂商要在SDK界面(SDK带UI界面)中显示传入人员的信息,其中包括人员头像。第三方厂商提供人员头像的完整url,SDK使用url将人员头像显示出来。后来选择使用libcurl开源库去实现url头像的下载,本文详细讲述相关细节并给出相关实现代码。
C++软件异常排查从入门到精通系列教程(核心精品专栏,订阅量已达8000多个,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++实战专栏(重点专栏,专栏文章已更新500多篇,订阅量已达6000多个,欢迎订阅,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/140824370C++ 软件开发从入门到实战(重点专栏,专栏文章已更新300多篇,欢迎订阅,持续更新中...)
https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_2276111.html
1、libcurl介绍
libcurl是一个免费开源的网络传输库(the multiprotocol file transfer library),支持ftp、ftps、http、https、telnet、ldap、pop3、smtp、rtmp、rtsp、smb等多种协议。libcurl中封装了支持这些协议的网络通信模块,支持跨平台,支持Windows,Unix,Linux等多个操作系统。libcurl提供了一套统一样式的API接口,我们不用关注各种协议下网络通信的实现细节,只需要调用这些API就能轻松地实现基于这些协议的数据通信。
如果我们自己通过socket套接字编程去实现ftp、http、https、smtp、rtmp、rtsp等常用协议下的通信,先要和服务器建立连接,然后再进行协议的协商,协商完成后进行数据通信。协议协商的过程以及Socket网络通信处理(包含各种通信异常的处理),会涉及到诸多的细节,会比较复杂,容易出错,质量也没法保证。一般我们会选择libcurl等开源库去实现这些协议下的数据通信,省时省力,通信质量也有保证。
本文将使用libcurl库去发http/https请求,去下载头像文件。以前我们在实现邮件发送功能时,也用到了libcurl库,使用其内部封装的SMTP协议发送邮件,相关实战案例,可以查看我的文章:
VC++调用libcurl开源库实现发送邮件的功能(附源码)https://blog.csdn.net/chenlycly/article/details/121318616
2、libcurl库源码下载与编译
可以到curl官网https://curl.se/,查看libcurl开源库的详细说明。
我们可以自行下载libcurl库的源码并进行编译,然后集成到项目中。到github的curl主页https://github.com/curl/curl,下载源码。
libcurl库及依赖库的下载与编译,在此就不详细介绍了,可以查看我之前写的完整编译过程的文章:
详解C++开源网络传输库libcurl的编译过程https://blog.csdn.net/chenlycly/article/details/140718865
3、调用libcurl库的API接口实现http/https请求发送,实现头像文件下载
对于C++程序,不支持直接通过url显示图片(在浏览器中打开的网页,则比较方便,内置一个url头像节点,即可显示图片),要在程序中显示图片,则需要先将图片下载到本地磁盘上,然后加载磁盘上的图片文件进行显示。
以CSDN网页中的图片为例:https://i-blog.csdnimg.cn/blog_migrate/e0355e98506ba6127924d711c1bf87f1.png,可以直接在浏览器中输入这个url,直接在浏览器中就可以看到图片:
从服务器下载图片并显示均是浏览器完成的。这个图片url是公开可见的,没有登录及权限控制,可以直接访问。在SDK项目中,第三方客户给的人员头像url,就是这种公开可访问的url。
我们直接使用libcurl将url对应的http/https请求发出去,然后在http/https请求回应中将头像文件数据返回回来,我们将图片数据保存到磁盘文件中,然后将图片显示在程序中。
先调用curl_easy_init接口初始化libcurl库,然后调用curl_easy_setopt(使用CURLOPT_URL选项)设置url请求地址(https://i-blog.csdnimg.cn/blog_migrate/e0355e98506ba6127924d711c1bf87f1.png,以CSDN图片链接为例),正是通过该url的前缀确定具体使用哪种协议,此处使用的是https协议:
CURLcode return_code;
CURL* curl = curl_easy_init();
char* pHeadPicUrl = "https://i-blog.csdnimg.cn/blog_migrate/e0355e98506ba6127924d711c1bf87f1.png"; // 此处以csdn中的图片为例
return_code = curl_easy_setopt(curl, CURLOPT_URL, pHeadPicUrl);
在使用相关协议完成数据交互时,可能还要设置一些其他的信息,比如用户名和密码、是否校验SSL证书等,都是通过调用curl_easy_setopt设置的:
return_code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
关闭ssl验证表示将不会验证对方(服务器)的SSL证书。这意味着,即使服务器的证书是自签名的、过期的、或者与请求的主机名不匹配,cURL也会继续建立连接,而不会因为证书问题而失败。
本例中要下载图片文件,在发送的url请求的回应中会携带图片文件数据,这样我们可以设置一个回调函数,用来接收图片文件数据的回调:
// Hook up data handling function.
return_code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
回调函数的实现如下:
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{size_t realsize = size * nmemb;struct MemoryStruct *mem = (struct MemoryStruct *)userp;mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);if (mem->memory == NULL) {/* out of memory! */printf("not enough memory (realloc returned NULL)\n");return 0;}memcpy(&(mem->memory[mem->size]), contents, realsize);mem->size += realsize;mem->memory[mem->size] = 0;return realsize;
}
然后调用curl_easy_getinfo接口获取http响应码:
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
看看是否是请求成功的200。返回200表示请求图片文件成功,则将回调过来的图片数据(保存在内存中),写到磁盘文件中,如下:
HANDLE handle;
handle = CreateFile(strHeadPicFilePath, GENERIC_WRITE, FILE_SHARE_READ,NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE)
{DWORD dwLastError = GetLastError();strLog.Format(_T("[DownloadHeadPicFunc] 创建图片文件失败:%s,GetLastError:%d."),strHeadPicFilePath, dwLastError);WriteLog(strLog);CloseHandle(handle);continue;
}DWORD dwNumByteWritten = 0;
WriteFile(handle, chunk.memory, chunk.size, &dwNumByteWritten, NULL);
CloseHandle(handle);
free(chunk.memory);
在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)
专栏1:(该精品技术专栏的订阅量已达到10000多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,已经更新到210篇以上!欢迎订阅!)
C++软件调试与异常排查从入门到精通系列文章汇总https://blog.csdn.net/chenlycly/article/details/125529931
本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,详细介绍分析C++软件问题的常用分析工具,以图文并茂的方式给出具体的项目问题实战分析实例(详细讲述分析排查过程,很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!
考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!
专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到300篇以上!
专栏2:(本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,订阅量已达8000多个,专栏文章已经更新到500多篇,持续更新中...)
C/C++实战进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、数据结构与算法、C++11及以上新特性(开源代码中可能会用到很多新特性(比如WebRTC开源库),日常编码中也会用到部分新特性,面试时也会频繁地涉及到,学习新特性很有必要)、常用C++开源库的介绍与使用(比如SQLite、libcurl、libwebsockets、libevent、jsoncpp/RapidJson、Redis、RabbitMQ、MongoDB、MQTT、ZooKeeper、OpenCV、FFmpeg、SDL、GStreamer、Live555、ReactOS等)、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(引发C++软件异常的常见原因分析与总结、排查C++软件异常的手段与方法、分析C++软件异常的基础知识、使用常用软件分析工具分析C++软件问题、多个项目实战问题分析案例分享等)、设计模式(单例模式、工厂模式、观察者模式、状态模式等)、网络基础知识与网络问题分析进阶内容(实战问题分析实例分享)等。本专栏的内容都是建立在项目实践的基础上,来源于项目实战,服务于项目实战,很有实战参考价值!
专栏3:
C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/131405795
常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!
专栏4:
VC++常用功能开发汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/124272585
将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。
专栏5:
C++ 软件开发从入门到精通(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_12695902.html
根据多年C++软件开发实践,详细地总结了C/C++软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。
4、发送图片url下载图片文件的完整代码展示
代码中通过libcurl库发送http/https请求,会调用到libcurl库中的curl_easy_init、curl_easy_setopt、curl_easy_perform等接口,要先包含libcurl库的头文件curl.h,然后引入libcurl的导入库libcurl.lib。
发送图片url下载图片文件的完整代码如下所示:
struct MemoryStruct {char *memory;size_t size;
};static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{size_t realsize = size * nmemb;struct MemoryStruct *mem = (struct MemoryStruct *)userp;mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);if (mem->memory == NULL) {/* out of memory! */printf("not enough memory (realloc returned NULL)\n");return 0;}memcpy(&(mem->memory[mem->size]), contents, realsize);mem->size += realsize;mem->memory[mem->size] = 0;return realsize;
}void DownLoadHeadPic()
{CURLcode return_code;CURL* curl = curl_easy_init();CString strLog;char* pHeadPicUrl = https://i-blog.csdnimg.cn/blog_migrate/e0355e98506ba6127924d711c1bf87f1.png"// 1、使用libcurl发送头像url请求(http请求),下载头像文件数据// Set remote URL.return_code = curl_easy_setopt(curl, CURLOPT_URL, pHeadPicUrl);//return_code = curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent);//return_code = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);return_code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);// Don't bother trying IPv6, which would increase DNS resolution time.return_code = curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);// Don't wait forever, time out after 30 seconds.return_code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);return_code = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);// Response information.int httpCode(0);//std::unique_ptr<std::string> httpData(new std::string());struct MemoryStruct chunk;chunk.memory = (char*)malloc(1); /* will be grown as needed by the realloc above */chunk.size = 0; /* no data at this point */// Hook up data handling function.return_code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);// Hook up data container (will be passed as the last parameter to the// callback handling function). Can be any pointer type, since it will// internally be passed as a void pointer.return_code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);// Run our HTTP GET command, capture the HTTP response code, and clean up.return_code = curl_easy_perform(curl);if (return_code != CURLE_OK){strLog.Format(_T("[DownloadHeadPicFunc] curl_easy_perform执行失败,返回的错误码:%d, 头像url:%s."),(int)return_code, CopyCharToCStringT(pHeadPicUrl));WriteConfLog(strLog);continue;}return_code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);if (return_code != CURLE_OK){strLog.Format(_T("[DownloadHeadPicFunc] curl_easy_getinfo执行失败,返回的错误码:%d, 头像url:%s."),(int)return_code, CopyCharToCStringT(pHeadPicUrl));WriteLog(strLog);continue;}else{if (httpCode != 200){strLog.Format(_T("[CVideoCtrlLogic::DownloadHeadPicFunc] httpCode错误码:%d, 头像url:%s."),httpCode, CopyCharToCStringT(pHeadPicUrl));WriteLog(strLog);continue;}}// 2、构建头像的名称,使用用户uuid加上url中的文件后缀名// 比如D:\xxxxx\47491a5147124ec8b698ae39c5540432.pngCString strHeadPicFilePath;// 此处指定头像文件的存放路径及头像文件名称的代码省略// 3、将头像文件数据写到磁盘文件中HANDLE handle;handle = CreateFile(strHeadPicFilePath, GENERIC_WRITE, FILE_SHARE_READ,NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (handle == INVALID_HANDLE_VALUE){DWORD dwLastError = GetLastError();strLog.Format(_T("[DownloadHeadPicFunc] 创建图片文件失败:%s,GetLastError:%d."),strHeadPicFilePath, dwLastError);WriteLog(strLog);CloseHandle(handle);continue;}DWORD dwNumByteWritten = 0;WriteFile(handle, chunk.memory, chunk.size, &dwNumByteWritten, NULL);CloseHandle(handle);free(chunk.memory);
}
5、使用libcurl发送https请求时可能会遇到的两个错误
如果发送的是https请求,可能会遇到两个错误,即curl_easy_perform返回两个错误码:
1)CURLE_UNSUPPORTED_PROTOCOL(当前库不支持协议)
当前库不支持https协议。https通信需要进行SSL协商,需要用于SSL协议协商的openssl库。应该是编译libcurl时选择的编译配置选项有问题,应该选择如下图中带OPENSSL的配置:
点击VS菜单栏中的生成 --> 配置管理器,即可以打开上图中的窗口。
2)CURLE_SSL_CACERT(SSL协商需要的CA证书有问题)
找不到SSL协议协商所需要的证书。发https请求进行SSL协商时,需要用到CA证书,如果本地没有CA证书,则curl_easy_perform会返回CURLE_SSL_CACERT错误。解决办法是,直接SSL证书验证选项关闭掉:
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);