Skip to content

Instantly share code, notes, and snippets.

@kyle-go
Last active December 17, 2015 01:59
Show Gist options
  • Save kyle-go/5532243 to your computer and use it in GitHub Desktop.
Save kyle-go/5532243 to your computer and use it in GitHub Desktop.
curl-httpdownload...
#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;
}
#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_
#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