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 }