libcurl 下载上传

近来一个新的项目需要使用到http。

本来用socket来写一个的,后来发现功能实在太简单,有点捉襟见肘。

于是改用libcur来做。

首先下载libcur的源码,然后配置:

1 ./configure --prefix=$HOME/csource/linux/ CFLAGS='-O2 -m32 -fPIC' --enable-optimize  --enable-static=libcurl.a --enable-ftp --without-zlib --disable-gopher --disable-rtsp --disable-dict --enable-proxy --disable-telnet  --enable-tftp   --disable-pop3   --disable-imap   --enable-smtp  --enable-ipv6  --enable-http -enable-crypto-auth  --without-gnutls --without-nss --without-ca-bundle --with-random=/dev/urandom

然后编译

1 make && make install

代码如下,使用一个c++类来管理

重点代码是DownloadFile和UploadFile

  1 #ifndef __MY_HTTP_CURL_H
  2 #define __MY_HTTP_CURL_H
  3 
  4 #include <string>
  5 
  6 typedef long long LongSize;
  7 
  8 typedef int (*pCallBack)(double dtotal, double dnow);
  9 
 10 class CMYHttpClient
 11 {
 12 public:
 13         CMYHttpClient();
 14         ~CMYHttpClient();
 15 
 16 public:
 17         /**
 18         * @brief 下载请求, 支持断点续传
 19         * @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
 20         * @param strFile 输入参数,本地存储的文件名
 21         * @param timeout 输入参数,超时限制,0为永久等待
 22         * @return 返回是否下载成功:true成功,false失败
 23         */
 24         bool DownloadFile(const char *strUrl, const char *strFile, pCallBack cb = NULL, int timeout = 0);
 25         /**
 26         * @brief 获取将要下载的文件的大小,失败返回-1,成功返回非负
 27         * @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
 28         * @return 返回文件的大小,失败返回-1
 29         */
 30         LongSize GetDownloadFileSize(const char *strUrl);
 31         /**
 32         * @brief 上载请求, 支持断点续传
 33         * @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
 34         * @param strFile 输入参数,请求上载的文件名
 35         * @param timeout 输入参数,超时限制,0为永久等待
 36         * @return 返回是否下载成功:true成功,false失败
 37         */
 38         bool UploadFile(const char *strUrl, const char *strFile, pCallBack cb = NULL, int timeout = 0);
 39 
 40 public:
 41         /**
 42         * @brief HTTP POST请求
 43         * @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
 44         * @param strPost 输入参数,使用如下格式para1=val1¶2=val2&…
 45         * @param strResponse 输出参数,返回的内容
 46         * @param nResponse 输入输出参数,输入缓冲区大小,返回读入缓冲区的内容大小
 47         * @param strFile 输入文件参数,返回的内容保存在这个文件中
 48         * @param strHeader 输入参数,需要额外指定的http头,如果输入为NULL,则忽略
 49         * @return 返回是否Post成功:0成功,非0失败
 50         */
 51         int Post(const char *strUrl, const char* strPost, size_t nPost,
 52                         const char *strFile, const char* strHeader);
 53         int Post(const char *strUrl, const char* strPost, size_t nPost,
 54                  char *strResponse, size_t &nResponse, const char *strHeader);
 55         int Post(const std::string &strUrl, const std::string &strPost,
 56                         const char *strFile, const char* strHeader);
 57         int Post(const std::string &strUrl, const std::string &strPost,
 58                 std::string &strResponse, const char *strHeader);
 59 
 60         /**
 61         * @brief HTTP GET请求
 62         * @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
 63         * @param strFile 输入文件参数,返回的内容保存在这个文件中
 64         * @param strResponse 输出参数,返回的内容
 65         * @param nResponse 输入输出参数,输入缓冲区大小,返回读入缓冲区的内容大小
 66         * @return 返回是否Get成功:0成功,非0失败
 67         */
 68         int Get(const char *strUrl, const char *strFile);
 69         int Get(const char *strUrl, std::string &strResponse);
 70         int Get(const char *strUrl, char *strResponse, size_t &nResponse);
 71         int Get(const std::string &strUrl, char *strResponse, size_t &nResponse);
 72         int Get(const std::string &strUrl, std::string &strResponse);
 73 
 74         /**
 75         * @brief HTTPS POST请求,无证书版本
 76         * @param strUrl 输入参数,请求的Url地址,如:https://www.alipay.com
 77         * @param strPost 输入参数,使用如下格式para1=val1¶2=val2&…
 78         * @param strResponse 输出参数,返回的内容
 79         * @param strHeader 输入参数,需要额外指定的http头,如果输入为NULL,则忽略
 80         * @param pCaPath 输入参数,为CA证书的路径.如果输入为NULL,则不验证服务器端证书的有效性.
 81         * @return 返回是否Post成功:0成功,非0失败
 82         */
 83         int Posts(const char *strUrl, const char *strPost, size_t nPost,
 84                         const char *strFile, const char *strHeader, const char *pCaPath);
 85         int Posts(const std::string &strUrl, const std::string &strPost,
 86                         const char *strFile, const char *strHeader, const char *pCaPath);
 87         int Posts(const char *strUrl, const char *strPost, size_t nPost,
 88                 char *strResponse, size_t &nResponse, const char *strHeader,
 89                 const char *pCaPath);
 90         int Posts(const std::string &strUrl, const std::string &strPost,
 91                         std::string &strResponse, const char *strHeader, const char *pCaPath);
 92 
 93         /**
 94         * @brief HTTPS GET请求,无证书版本
 95         * @param strUrl 输入参数,请求的Url地址,如:https://www.alipay.com
 96         * @param strResponse 输出参数,返回的内容
 97         * @param pCaPath 输入参数,为CA证书的路径.如果输入为NULL,则不验证服务器端证书的有效性.
 98         * @return 返回是否Post成功
 99         */
100         int Gets(const std::string &strUrl, std::string &strResponse,
101                         const char *pCaPath = NULL);
102 
103 public:
104         void SetDebug(bool bDebug);
105         void AbortOperation(bool bAbort = true) { m_bAbort = bAbort; }
106 
107 private:
108         bool m_bDebug;          //是否打开debug
109         bool m_bAbort;          //是否放弃操作
110 
111         std::string m_strFile;  //需要下载的文件或者上传的文件
112 
113         pCallBack       m_fCB;  //上传或者下载时的进度回调函数
114         LongSize        m_lUploadPos;   //上传文件的进度
115 
116         static int OnProgress(void *pClient, curl_off_t dltotal, curl_off_t dlnow,
117                         curl_off_t ultotal, curl_off_t ulnow);
118         static size_t OnWriteBuffer2File(char* buffer, size_t size, size_t nmemb, void* arg);
119         static size_t OnReadFile2Buffer(char *buffer, size_t size, size_t nmemb, void* arg);
120 };
121 
122 #endif

几个主要方法,其中https的编译需要openssl,所以我没做支持。

  1 #include "curl/curl.h"
  2 #include <string>
  3 #include <fstream>
  4 #include <iostream>
  5 #include <sstream>
  6 
  7 #include "HttpClient.h"
  8 
  9 
 10 using namespace std;
 11 
 12 CMYHttpClient::CMYHttpClient() :
 13 #ifdef _DEBUG
 14         m_bDebug(true),
 15 #else
 16         m_bDebug(false),
 17 #endif
 18         m_bAbort(false),
 19         m_fCB(NULL),
 20         m_lUploadPos(0)
 21 {
 22 
 23 }
 24 
 25 CMYHttpClient::~CMYHttpClient()
 26 {
 27 
 28 }
 29 
 30 static int OnDebug(CURL *, curl_infotype itype, char *pData, size_t size, void *)
 31 {
 32         if(itype == CURLINFO_TEXT)
 33         {
 34                 //printf("[TEXT]%s\n", pData);
 35         }
 36         else if(itype == CURLINFO_HEADER_IN)
 37         {
 38                 printf("[HEADER_IN]%s\n", pData);
 39         }
 40         else if(itype == CURLINFO_HEADER_OUT)
 41         {
 42                 printf("[HEADER_OUT]%s\n", pData);
 43         }
 44         else if(itype == CURLINFO_DATA_IN)
 45         {
 46                 printf("[DATA_IN]%s\n", pData);
 47         }
 48         else if(itype == CURLINFO_DATA_OUT)
 49         {
 50                 printf("[DATA_OUT]%s\n", pData);
 51         }
 52 
 53         return 0;
 54 }
 55 
 56 int CRGHttpClient::OnProgress(void *pClient, curl_off_t dltotal, curl_off_t dlnow,
 57                 curl_off_t ultotal, curl_off_t ulnow)
 58 {
 59         CMYHttpClient* pThis = (CMYHttpClient *)pClient;
 60         if(NULL == pThis || pThis->m_bAbort)
 61         {
 62                 //Returning a non-zero value from this callback will cause libcurl to abort
 63                 //the transfer and return CURLE_ABORTED_BY_CALLBACK.
 64                 return __LINE__;
 65         }
 66         else
 67         {
 68                 if(pThis->m_fCB)
 69                 {
 70                         pThis->m_fCB((double)dltotal, (double)dlnow);
 71                 }
 72 #ifdef _DEBUG
 73                 if(dltotal)
 74                 {
 75                         cout << "total bytes expects to download: " << dltotal
 76                                 << " and downloaded: " << dlnow << "\n";
 77                 }
 78                 else if(ultotal)
 79                 {
 80                         cout << "total bytes expects to upload: " << ultotal
 81                                 << " and uploaded: " << ulnow << "\n";
 82                 }
 83 #endif
 84 
 85                 return 0;
 86         }
 87 }
 88 
 89 size_t CMYHttpClient::OnWriteBuffer2File(char* buffer, size_t size, size_t nmemb, void* arg)
 90 {
 91         CMYHttpClient* pThis = (CMYHttpClient *)arg;
 92         if(NULL == pThis || pThis->m_bAbort ||
 93                         NULL == buffer)
 94         {
 95                 return 0;
 96         }
 97 
 98         const char* strFile = pThis->m_strFile.c_str();
 99 
100         ofstream outfile(strFile, ofstream::binary | ofstream::app);
101         if(outfile.good())
102         {
103                 const char* pData = (const char *)buffer;
104                 outfile.write(pData, size * nmemb);
105 
106                 return nmemb;
107         }
108 
109         return 0;
110 }
111 
112 size_t CMYHttpClient::OnReadFile2Buffer(char *buffer, size_t size, size_t nmemb, void* arg)
113 {
114         CMYHttpClient* pThis = (CMYHttpClient *)arg;
115         if(NULL == pThis || NULL == buffer)
116         {
117                 return 0;
118         }
119 
120         ifstream infile(pThis->m_strFile.c_str(), ifstream::binary);
121         if(infile.good())
122         {
123                 infile.seekg(pThis->m_lUploadPos, infile.beg);
124                 if(infile.eof() == false)
125                 {
126                         infile.read(buffer, size * nmemb);
127                         pThis->m_lUploadPos += infile.gcount();
128                         return infile.gcount();
129                 }
130         }
131 
132         return 0;
133 }
134 
135 LongSize CMYHttpClient::GetDownloadFileSize(const char* strUrl)
136 {
137         if(NULL == strUrl)
138                 return -1;
139 
140         CURL* curl = curl_easy_init();
141         if(NULL == curl)
142                 return -1;
143 
144         curl_easy_setopt(curl, CURLOPT_URL, strUrl);
145         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
146         curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
147         CURLcode res = curl_easy_perform(curl);
148         if(res == CURLE_OK) {
149                 double sz = 0;
150                 res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &sz);
151 
152                 curl_easy_cleanup(curl);
153 
154                 return (LongSize)sz;
155         }
156         else
157         {
158                 cout << curl_easy_strerror(res) << endl;
159         }
160 
161         curl_easy_cleanup(curl);
162 
163         return -1;
164 }
165 
166 bool CMYHttpClient::DownloadFile(const char* strUrl, const char* strFile, pCallBack cb, int timeout)
167 {
168         if(NULL == strUrl || NULL == strFile)
169         {
170                 return false;
171         }
172 
173         //初始化curl库句柄
174         CURL* curl = curl_easy_init();
175         if(NULL == curl)
176         {
177                 return false;
178         }
179 
180         m_strFile = string(strFile) + ".dl";
181         m_fCB = cb;
182         //支持断点续传,先获取文件大小,如果文件存在并且非空,则断点续传
183         ifstream infile(m_strFile.c_str(), ifstream::binary);
184         if(infile.good())
185         {
186                 infile.seekg(0, infile.end);
187                 int length = infile.tellg();
188                 infile.seekg(0, infile.beg);
189                 if(length > 0)
190                 {
191                         stringstream ss;
192                         ss << length;
193                         ss << "-";
194                         LongSize ltotal = GetDownloadFileSize(strUrl);
195                         if(ltotal > 0)
196                                 ss << ltotal;
197                         string srange;
198                         ss >> srange;
199                         curl_easy_setopt(curl, CURLOPT_RANGE, srange.c_str());
200                 }
201         }
202         infile.close();
203 
204         CURLcode res;
205         if(m_bDebug)
206         {
207                 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
208                 curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);
209         }
210 
211         curl_easy_setopt(curl, CURLOPT_URL, strUrl);
212         curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
213         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteBuffer2File);
214         curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
215 
216         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
217         curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, OnProgress);
218         curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this);
219 
220         /**
221         * 当多个线程都使用超时处理的时候,同时主线程中有sleep或是wait等操作。
222         * 如果不设置这个选项,libcurl将会发信号打断这个wait从而导致程序退出。
223         */
224         curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
225         curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 4); //wait for 4 seconds to connect to server
226         curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);       //0 means block always
227 
228         AbortOperation(false);  //reset abort flag
229         res = curl_easy_perform(curl);
230 
231         curl_easy_cleanup(curl);
232 
233         if(res != CURLE_OK)
234         {
235                 cout << curl_easy_strerror(res);
236         }
237         else
238         {
239 #ifdef _WIN32
240                 DeleteFile(strFile);
241                 MoveFile(m_strFile.c_str(), strFile);
242 #else
243                 unlink(strFile);
244                 rename(m_strFile.c_str(), strFile);
245 #endif
246         }
247 
248 
249         m_strFile.clear();
250         m_fCB = NULL;
251 
252         return res == CURLE_OK;
253 }
254 
255 bool CMYHttpClient::UploadFile(const char *strUrl, const char *strFile, pCallBack cb, int timeout)
256 {
257         if(NULL == strUrl || NULL == strFile)
258         {
259                 return false;
260         }
261 
262         //初始化curl库句柄
263         CURL* curl = curl_easy_init();
264         if(NULL == curl)
265         {
266                 return false;
267         }
268 
269         CURLcode res;
270         m_fCB = cb;
271         m_strFile = strFile;
272         m_lUploadPos = 0;
273 
274         curl_easy_setopt(curl, CURLOPT_URL, strUrl);
275         curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
276 
277         curl_easy_setopt(curl, CURLOPT_READFUNCTION, OnReadFile2Buffer);
278         curl_easy_setopt(curl, CURLOPT_READDATA, this);
279         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
280 
281         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
282         curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, OnProgress);
283         curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this);
284 
285         curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
286         curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 4); //wait for 4 seconds to connect to server
287         curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);       //0 means block always
288 
289         AbortOperation(false);  //reset abort flag
290         res = curl_easy_perform(curl);
291 
292         curl_easy_cleanup(curl);
293 
294         m_strFile.clear();
295         m_fCB = NULL;
296 
297         if(res != CURLE_OK)
298         {
299                 cout << curl_easy_strerror(res);
300         }
301 
302         return res == CURLE_OK;
303 }

 

posted @ 2014-08-29 18:38  Jojodru  阅读(3843)  评论(0编辑  收藏  举报