c++使用libcurl实现断点续传
libcurl实现断点续传大致需要两个关键点:
1:使用GetFileSize接口获取本地已缓存文件的大小。
2:通过curl_easy_setopt接口的CURLOPT_RESUME_FROM_LARGE参数,将获取的大小设置到开始下载的起点。
下面demo代码仅提供思路,需要优化才能使用于线上项目(比如本地缓存文件的有效性就是必须要考虑的一个问题)
实现的demo代码:
void OpenConsole() { AllocConsole(); freopen("CONIN$", "r+t", stdin); freopen("CONOUT$", "w+t", stdout); } struct DownloadAttr { curl_off_t file_size; CURL* curl; }; size_t WriteCallback(char * ptr, size_t size, size_t nmemb, void * userdata) { HANDLE& hFile = (HANDLE)userdata; DWORD write_byte = 0; if (!::WriteFile(hFile, ptr, size * nmemb, &write_byte, NULL)) return 0; return write_byte; } int ProgressCallback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { if (dltotal == 0) return 0; DownloadAttr* download_attr = (DownloadAttr*)clientp; int progress = (double)(dlnow + download_attr->file_size) / (dltotal + download_attr->file_size) * 100; static int last_progress = 0; if (last_progress != progress) { last_progress = progress; double speed = 0; curl_easy_getinfo(download_attr->curl, CURLINFO_SPEED_DOWNLOAD, &speed); speed /= 1024; std::cout << "已下载:" << progress << "%" << " 下载速度:" << speed << "kb/s" << std::endl; } return 0; } bool BreakpointResumeDownload(std::string url, std::wstring save_path) { HANDLE hFile = ::CreateFile(save_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) return false; CURL* curl = curl_easy_init(); if (curl == nullptr) { ::CloseHandle(hFile); return false; } curl_off_t file_size = ::GetFileSize(hFile, nullptr); ::SetFilePointer(hFile, 0, 0, FILE_END); DownloadAttr download_attr = { file_size,curl }; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, hFile); curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &download_attr); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, file_size); CURLcode curl_code = curl_easy_perform(curl); curl_easy_cleanup(curl); ::CloseHandle(hFile); if (curl_code != CURLE_OK) { ::DeleteFile(save_path.c_str()); return false; } return true; } int WINAPI _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) { OpenConsole(); ::CoInitialize(nullptr); curl_global_init(CURL_GLOBAL_ALL); std::cout << "开始下载:" << std::endl; BreakpointResumeDownload( "https://dldir1.qq.com/music/clntupate/QQMusicSetup.exe", L"D:\test.exe"); std::cout << "下载完成:" << std::endl; curl_global_cleanup(); ::CoUninitialize(); getchar(); return 0; }
demo演示:
为演示续传,演示图为下载开头和结尾部分,中间部分没有截图