天天看点

libcurl实现断点续传

实现断点续传主要就是通过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