当前位置:首页 > c++ > c++使用libcurl实现断点续传

c++使用libcurl实现断点续传

xuwenyan7个月前 (02-22)c++1230

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演示:

为演示续传,演示图为下载开头和结尾部分,中间部分没有截图






    文章作者:xuwenyan
    版权声明:本文为本站原创文章,转载请注明出处,非常感谢,如版权漏申明或您觉得任何有异议的地方欢迎与本站取得联系。

    相关文章

    C++ 获取进程所在目录(进程全路径)

    C++ 获取进程所在目录(进程全路径)

    打开windows任务管理器,会看到很多的进程在运行,随机挑选一个,如何通过c++代码获取某一个进程的所在全路径呢?这也是在windows软件开发中经常遇到的需求。通过进程名获取进程全路径由于可能很多...

    排序算法-快速排序

    排序算法-快速排序

    排序算法的思想非常简单,在待排序的数列中,我们首先要找一个数字作为基准数(这只是个专用名词)。为了方便,我们一般选择第 1 个数字作为基准数(其实选择第几个并没有关系)。接下来我们需要把这个待排序的数...

    排序算法-冒泡排序

    排序算法-冒泡排序

    冒泡排序也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法...

    排序算法-选择排序

    排序算法-选择排序

    选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。时间复杂度O(n²)最坏情况合适发生?...

    std::make_shared有什么好处?

    std::make_shared有什么好处?

    为什么使用std::make_shared,std::make_shared有什么好处?如下: 更美观的代码std::shared_ptr<Node> ptr(new Node);std...

    发表评论

    访客

    ◎欢迎参与讨论,请在这里发表您的看法和观点。