当前位置:首页 > c++ > 正文内容

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

xuwenyan2年前 (2022-02-22)c++1391

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

    扫描二维码推送至手机访问。

    版权声明:本文由艺文笔记发布,如需转载请注明出处。

    本文链接:https://www.xuwenyan.com/archives/2705

    分享给朋友:

    “c++使用libcurl实现断点续传” 的相关文章

    7z的简介和使用

    7z的简介和使用

    7z是一个支持多种压缩格式的开源项目,由Igor Pavlov开发,源码下载位置:https://www.7-zip.org/download.html源码结构项目源码目录结构是如下图:Asm包含主要算法实现的汇编代码,直接使用汇编的好处是可以提高运行效率,当然这对跨平台的支持不是很好。C主要是算法...

    C++如何获取控制台程序的输出内容?

    C++如何获取控制台程序的输出内容?

    很多工具程序(如ffmpeg)的进度显示往往都是以控制台字符显示的方法,我们可能需要调用这种控制台工具去完成工作,但同时又希望以友好的ui界面去显示当前的工作状态(如进度)。此时我们能想到的就是运行控制台程序,然后以某种方式去获取到控制台程序的输出,然后转换到我们的ui界面上去显示。 有多种...

    使用GDI、MFC_GDI、GDI+绘制数组RGBA序列

    使用GDI、MFC_GDI、GDI+绘制数组RGBA序列

    学习ffmpeg时遇到一个问题,ffmpeg解码出RGB颜色后怎么绘制到屏幕上,于是将GDI、MFC_GDI、GDI+等方式都记录一下 1:注意按windows的要求,R、G、B、A顺序要调整为B、G、R、A 。 2:GDI不支持透明通道A,透明通道A的值读进去以后没有作用。想要支持透...

    VC的ATL工程向导同时生成一个PS工程是做什么的?可以不要吗?

    VC的ATL工程向导同时生成一个PS工程是做什么的?可以不要吗?

    例如,我用VC2015的工程向导新建一个ATL的工程名字叫myAtl,那么VC会同时给我生成一个叫做myAtlPS的工程。这个myAtlPS工程是做什么的?什么情况下可以不需要它?什么情况下它又是必须存在的? PS工程是什么?可以不要吗? 这个PS工程叫做代理与存根(proxy&nbs...

    ATL实现windows右键菜单扩展(ContextMenu)

    ATL实现windows右键菜单扩展(ContextMenu)

    右键菜单,即用户右击shell对象时弹出的上下文菜单(context menu)。本文记录了如何创建右键菜单的基本过程,跟着步骤一步一步来,即可创建出一个右键菜单工程。第一步,新建一个ATL工程Visual Studio—>新建项目—>ATL—>使用默认配置(一直按下一步即可)。注...

    static_assert和assert有什么区别?

    static_assert和assert有什么区别?

    static_assert和assert都是断言,都可用于判断一个条件是否成立,并且在条件不成立时及时给出错误提示。那它们用什么不同和需要注意的地方呢? 1:static_assert在编译期执行,而assert在运行期执行。2:static_assert无论在debug模式还是relea...