Last active
December 17, 2015 01:59
-
-
Save kyle-go/5532243 to your computer and use it in GitHub Desktop.
curl-httpdownload...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "curl_httpdownload.h" | |
#include <stdlib.h> | |
#include <sys/stat.h> | |
#include <curl/curl.h> | |
#include "scope.h" | |
#ifdef _DEBUG | |
#pragma comment(lib, "libcurl-d.lib") | |
#else | |
#pragma comment(lib, "libcurl.lib") | |
#endif | |
struct PROGRESS_DATA | |
{ | |
curl_off_t from; | |
std::function<void(double)> callback; | |
}; | |
/* 从http头部获取文件size*/ | |
size_t getcontentlengthfunc(const char *ptr, size_t size, size_t nmemb, void *stream) { | |
int r; | |
long len = 0; | |
/* _snscanf() is Win32 specific */ | |
r = _snscanf_s(ptr, size * nmemb, "Content-Length: %ld\n", &len); | |
//r = sscanf(ptr, "Content-Length: %ld\n", &len); | |
if (r) /* Microsoft: we don't read the specs */ | |
*((long *) stream) = len; | |
return size * nmemb; | |
} | |
int progress_func(void* ptr, double rDlTotal, double rDlNow, double rUlTotal, double rUlNow) | |
{ | |
PROGRESS_DATA* pd = static_cast<PROGRESS_DATA*>(ptr); | |
if (rDlTotal >= 1.0 && pd->callback) | |
pd->callback((rDlNow + pd->from)*1.0/(rDlTotal + pd->from)); | |
return 0; | |
} | |
// 下载文件函数 | |
int download(CURL *curlhandle, const char * remotepath, const char * localpath, std::function<void(double)> callback) | |
{ | |
curl_off_t local_file_len = 0 ; | |
long filesize =0 ; | |
struct stat file_info; | |
int use_resume = 0; | |
if(stat(localpath, &file_info) == 0) | |
{ | |
local_file_len = file_info.st_size; | |
use_resume = 1; | |
} | |
//采用追加方式打开文件,便于实现文件断点续传工作 | |
FILE *f = NULL; | |
fopen_s(&f, localpath, "ab+"); | |
if (f == NULL) { | |
perror(NULL); | |
return ERROR_TMP_FILE_ERROR; | |
} | |
PROGRESS_DATA pd = {local_file_len, callback}; | |
curl_easy_setopt(curlhandle, CURLOPT_URL, remotepath); | |
curl_easy_setopt(curlhandle, CURLOPT_CONNECTTIMEOUT, 90); // 设置连接超时,单位秒 | |
curl_easy_setopt(curlhandle, CURLOPT_TIMEOUT, 90); // 设置连接超时,单位秒 | |
//设置http 头部处理函数 | |
curl_easy_setopt(curlhandle, CURLOPT_HEADERFUNCTION, getcontentlengthfunc); | |
curl_easy_setopt(curlhandle, CURLOPT_HEADERDATA, &filesize); | |
// 设置文件续传的位置给libcurl | |
curl_easy_setopt(curlhandle, CURLOPT_RESUME_FROM, use_resume?local_file_len:0); | |
curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, f); | |
curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, fwrite); | |
//回调进度条函数 | |
curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 0); | |
curl_easy_setopt(curlhandle, CURLOPT_PROGRESSFUNCTION, progress_func); | |
curl_easy_setopt(curlhandle, CURLOPT_PROGRESSDATA, &pd); | |
curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L); | |
CURLcode ret = curl_easy_perform(curlhandle); | |
fclose(f); | |
return ret; | |
} | |
int curl_httpdownload_real(const char * remotepath, const char * localpath, std::function<void(double)> callback) | |
{ | |
curl_global_init(CURL_GLOBAL_ALL); | |
SCLOUD_SCOPE_EXIT([]{curl_global_cleanup();}); | |
CURL *curlhandle = curl_easy_init(); | |
scloud::ScopeGuard curl_easy_scope([curlhandle]{curl_easy_cleanup(curlhandle);}); | |
if (!curlhandle) { | |
curl_easy_scope.cancel(); | |
return ERROR_CURL_INIT_ERROR; | |
} | |
return download(curlhandle , remotepath, localpath, callback); | |
} | |
int curl_httpdownload( const std::string& url, | |
const std::string& file, | |
std::function<void(double)> callback, | |
int timeout/* = 30*/) | |
{ | |
int ret = 0; | |
int my_timeout = timeout >= 30? timeout:30; | |
std::string tmp_file = []()->std::string{ | |
TCHAR szTmpFile[MAX_PATH] = {0}; | |
TCHAR szTmpPath[MAX_PATH] = {0}; | |
GetTempPath(MAX_PATH, szTmpPath); | |
GetTempFileName(szTmpPath, "http", 0, szTmpFile); | |
return szTmpFile; | |
}(); | |
for (;;) | |
{ | |
ret = curl_httpdownload_real(url.c_str(), tmp_file.c_str(), callback); | |
if (0 == ret) | |
{ | |
SetFileAttributes(file.c_str(), FILE_ATTRIBUTE_NORMAL); | |
DeleteFile(file.c_str()); | |
if (MoveFile(tmp_file.c_str(), file.c_str())) | |
{ | |
//succeed. | |
if (callback) | |
callback(1.0); | |
return 0; | |
} | |
return GetLastError(); | |
} | |
Sleep(3000); | |
my_timeout -= 3; | |
if(my_timeout <= 0) | |
break; | |
} | |
return ret; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CURL_HTTPDOWNLOAD_H_ | |
#define CURL_HTTPDOWNLOAD_H_ | |
#include <string> | |
#include <functional> | |
enum { | |
ERROR_CONTENT_LENGTH_LESS_THAN_ZERO = 9527, | |
ERROR_WTF_NO_DATA, | |
ERROR_TMP_FILE_ERROR, | |
ERROR_CURL_INIT_ERROR, | |
}; | |
int curl_httpdownload( const std::string& url, | |
const std::string& file, | |
std::function<void(double)> callback, | |
int timeout = 30); | |
#endif //CURL_HTTPDOWNLOAD_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef SCOPE_H_ | |
#define SCOPE_H_ | |
#include <functional> | |
namespace scloud{ | |
class ScopeGuard | |
{ | |
public: | |
explicit ScopeGuard(std::function<void(void)> onExitScope) | |
: doExit_(onExitScope), cancel_(false){} | |
~ScopeGuard() { | |
if(!cancel_) { | |
doExit_(); | |
} | |
} | |
void cancel() { | |
cancel_ = true; | |
} | |
private: | |
//disable copy | |
ScopeGuard(const ScopeGuard&); | |
void operator=(const ScopeGuard&); | |
std::function<void(void)> doExit_; | |
bool cancel_; | |
}; | |
}//end namespace | |
#define SCLOUD_CAT_STRING(name, line) name##line | |
#define SCLOUD_XSOCPEGUARD_LOCK_CAT(name, line) SCLOUD_CAT_STRING(name,line) | |
#define SCLOUD_SCOPE_EXIT scloud::ScopeGuard SCLOUD_XSOCPEGUARD_LOCK_CAT(ScopeGuard, __LINE__) | |
#endif //SCOPE_H_ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment