实现断点续传主要就是通过curl_easy_setopt设置好CURLOPT_RESUME_FROM_LARGE属性完成
首先获取已下载文件大小,然后设置CURLOPT_RESUME_FROM_LARGE属性,从指定字节开始下载
#include <sys/stat.h>
// Get the local file size,return -1 if failed
_off_t getLocalFileLength(string path)
{
_off_t ret;
struct stat fileStat;
ret = stat(path.c_str(), &fileStat);
if (ret == 0)
{
return fileStat.st_size;
}
return ret;
}
curl_off_t resumeByte = getLocalFileLength("test10.zip"); // 本地已下载文件的字节大小,确保resumeByte类型为curl_off_t(Windows平台为long long,Mac为long)否则容易出错
int ret = curl_easy_setopt(easy_handle, CURLOPT_RESUME_FROM_LARGE, resumeByte);
resumeByte的类型不对或者超过了服务器文件的Content-Length时,curl_easy_perform都会返回错误
下次开始时,将会从指定的字节位置开始下载。
计算下载进度时需要注意一点,dltotal得到的不是服务器上文件的大小,而是还需要下载字节的大小,也就是说dototal并不是test10.zip这个文件的大小,而是test10.zip的大小减去本地已经下载的大小。那么如何得到服务器上的文件大小,我们可以通过libcurl发送请求header,返回的Content-Length就是文件大小
// 获取服务器上的文件大小
double getDownloadFileLength(string url)
{
CURL *easy_handle = NULL;
int ret = CURLE_OK;
double size = -1;
do
{
easy_handle = curl_easy_init();
if (!easy_handle)
{
printf("easy_handle init error\n");
break;
}
// 仅获取HTTP头
ret = curl_easy_setopt(easy_handle, CURLOPT_URL, url.c_str());
ret |= curl_easy_setopt(easy_handle, CURLOPT_HEADER, 1L);
ret |= curl_easy_setopt(easy_handle, CURLOPT_NOBODY, 1L);
if (ret != CURLE_OK)
{
break;
}
ret = curl_easy_perform(easy_handle);
if (ret != CURLE_OK)
{
break;
}
// 没有查询到的话size=-1
ret = curl_easy_getinfo(easy_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size);
if (ret != CURLE_OK)
{
break;
}
} while (0);
curl_easy_cleanup(easy_handle);
return size;
}
整个工程的GitHub地址:https://github.com/forzxy/HttpClient