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

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

xuwenyan11个月前 (02-22)c++592

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实现断点续传” 的相关文章

    使用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...

    uafxcwd.lib(afxmem.obj) : error LNK2005:

    uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)"解决办法

    如果在编译MFC程序的时候出现下列及类似的错误: 1˃uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) 已经在 LIBCMTD.lib(new...

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

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

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

    static_assert和assert有什么区别?

    static_assert和assert有什么区别?

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

    堆和栈有什么区别?哪个比较快?

    堆和栈有什么区别?哪个比较快?

    我们都知道,windows内存区域无外乎堆、栈、静态区、常量区,下面主要说一说堆和栈。 首先我们要注意一个容易混淆的点,我们经常会听人把堆栈连起来说,通常堆栈指的是栈,而不是堆。 什么是堆? 需要程序员自己申请,并指明大小。 什么是栈? 由编译器自动分配释放 ,存放为...