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演示:
为演示续传,演示图为下载开头和结尾部分,中间部分没有截图


作者:徐文焱