Cocos2d-3.10 使用Curl异步下载文件(断点续传)

因为要后台异步下载文件,所以研究了下Curl去下载文件,

大部分坑已踩完,可以正常使用,有需要的可以借鉴下,下载大文件的话,里面的有几个参数初始大小改下就可以。

本篇参考http://blog.csdn.net/vpingchangxin/article/details/22309067,这个2.x的,这里用3.10版本改造了下

转载请注明出处:Cocos2d-3.10 使用Curl异步下载文件(断点续传)

效果图如下:


微笑废话不多说,贴代码:

1.创建layer,添加进度条UI

#pragma once
#ifndef _LOADING_LAYER_H_
#define _LOADING_LAYER_H_

#include "cocos2d.h"
#include "CurlDown.h"
#include "cocostudio/CocoStudio.h"
#include "ui/CocosGUI.h"
USING_NS_CC;
using namespace cocos2d::ui;


class Loading :public Layer,public CurlDown
{
public:
	Loading();
	~Loading();

	static cocos2d::Scene* createScene();

	virtual bool init();
	CREATE_FUNC(Loading);

	virtual void onProgress(double percent, string totalSize, string downSize, string speed) override;
	virtual void onSuccess(bool isSuccess, string filefullPath) override;

private:
	int _count;
	Node* rootNode;
	Label* _label2;
	Label* _label4;
	Label* _label6;
};	

#endif


#include "LoadingLayer.h"



Loading::Loading()
{
}

Loading::~Loading()
{
}

cocos2d::Scene * Loading::createScene()
{
	auto scene = Scene::create();

	auto layer = Loading::create();

	scene->addChild(layer);

	return scene;
}


bool Loading::init()
{
	if (!Layer::init())
	{
		return false;
	}

	Size visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 origin = Director::getInstance()->getVisibleOrigin();

	rootNode = CSLoader::createNode("LoadingLayer.csb");
	addChild(rootNode);
	_label2 = Label::create();
	_label2->setTextColor(Color4B::BLUE);
	_label2->setPosition(Vec2(250, 180));

	_label4 = Label::create();
	_label4->setTextColor(Color4B::BLUE);
	_label4->setPosition(Vec2(434, 180));

	_label6 = Label::create();
	_label6->setTextColor(Color4B::BLUE);
	_label6->setPosition(Vec2(666, 180));
	this->addChild(_label2);
	this->addChild(_label4);
	this->addChild(_label6);
	downStart(); //开始下载文件

	return true;
}

void Loading::onProgress(double percent, string totalSize, string downSize, string speed)
{
	auto loadingBar = static_cast<LoadingBar*>(rootNode->getChildByName("LoadingBar_1"));

	loadingBar->setPercent(percent);
	_label2->setString(totalSize);
	_label4->setString(downSize);
	_label6->setString(speed);
}

void Loading::onSuccess(bool isSuccess, string filefullPath)
{
	if (isSuccess)
	{
		MessageBox("下载成功", "提示");
	}
}

下面才是本程序的核心代码:

#pragma once
#ifndef __download__CurlDown__
#define __download__CurlDown__
#include <string>
#include "cocos2d.h"
#include <thread>
#include "../external/curl/include/win32/curl/curl.h"
using namespace std;
USING_NS_CC;
#pragma comment(lib, "libcurl_imp.lib")

/************************************************************************/
/* libcurl download file                                                */
/************************************************************************/
class CurlDown 
{
public:
	CurlDown();
	~CurlDown();
	//入口函数
	void downStart();
	//下载控制
	void downloadControler();
	//下载
	bool download(long timeout);
	// 获取远程下载文件的大小
	long getDownloadFileLenth();
	// 读取本地文件已下载大小
	long getLocalFileLength();

	virtual void onProgress(double percent, string totalSize, string downSize,string speed) {};
	virtual void onSuccess(bool isSuccess, string filefullPath) { rename(filefullPath.c_str(), (mFilePath.substr(0, mFilePath.find(mFileName))+"res.zip").c_str()); };
	
public:
	string mFilePath; // 本地存储地址
	double mFileLenth; // 下载文件大小
	string mDownloadUrl; // 下载URL
	CURL *libcurl;

private:
	string mFileName; // 下载文件名称
	bool isStop;
	
};

#endif

#include "CurlDown.h"
#include "io.h"

CurlDown::CurlDown()
{
	mFileName = "res.temp";
	mFilePath = FileUtils::getInstance()->getSearchPaths()[0]+"download/res.temp";
	mDownloadUrl = "http://127.0.0.1:8080/download";
	mFileLenth=0;
}

CurlDown::~CurlDown()
{
}

static string getSize(double size)
{
	string unit = "";
	char tsize[10]="";
	if (size > 1024 * 1024 * 1024)
	{
		unit = "G";
		size /= 1024 * 1024 * 1024;
	}
	else if (size > 1024 * 1024)
	{
		unit = "M";
		size /= 1024 * 1024;
	}
	else if (size > 1024)
	{
		unit = "KB";
		size /= 1024;
	}
	sprintf(tsize, "%.1f", size);//保留1位小数(可自行设置)
	string res = tsize + unit;
	return res;
}

static size_t write_func(void *ptr, size_t size, size_t nmemb, void *userdata) {
	FILE *fp = (FILE*)userdata;
	size_t written = fwrite(ptr, size, nmemb, fp);
	return written;
}

static size_t getcontentlengthfunc(void *ptr, size_t size, size_t nmemb, void *data)
{
	return (size_t)(size * nmemb);
}


/************************************************************************/
/*  
这个回调函数可以告诉我们有多少数据需要传输以及传输了多少数据,单位是字节。
totalToDownload是需要下载的总字节数(这里不包括本地已下载的一部分),nowDownloaded是已经下载的字节数(指的是totalToDownload中已下载多少)。
totalToUpLoad是将要上传的字节数,nowUpLoaded是已经上传的字节数。如果你仅仅下载数据的话,那么ultotal,ulnow将会是0,反之,
如果你仅仅上传的话,那么dltotal和dlnow也会是0。clientp为用户自定义参数,
通过设置CURLOPT_XFERINFODATA属性来传递。此函数返回非0值将会中断传输,错误代码是CURLE_ABORTED_BY_CALLBACK 
*/
/************************************************************************/
static int progress_func(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded) {
	CurlDown* curlDown = (CurlDown*)ptr;
	CURL *easy_handle = curlDown->libcurl;

	char timeFormat[12] = "";
	double speed; //下载速度
	string unit = "B";
	curl_easy_getinfo(easy_handle, CURLINFO_SPEED_DOWNLOAD, &speed); // curl_get_info必须在curl_easy_perform之后调用
	if (speed != 0)
	{
		// Time remaining  
		double leftTime = (curlDown->mFileLenth - nowDownloaded - curlDown->getLocalFileLength()) / speed; //(总大小-已下载大小-本地存储大小)/速度
		int hours = leftTime / 3600;
		int minutes = (leftTime - hours * 3600) / 60;
		int seconds = leftTime - hours * 3600 - minutes * 60;
		sprintf(timeFormat, "%02d:%02d:%02d", hours, minutes, seconds);//剩余时间格式化
	}

	if (speed > 1024 * 1024 * 1024)
	{
		unit = "G";
		speed /= 1024 * 1024 * 1024;
	}
	else if (speed > 1024 * 1024)
	{
		unit = "M";
		speed /= 1024 * 1024;
	}
	else if (speed > 1024)
	{
		unit = "KB";
		speed /= 1024;
	}	
	log("speed:%.2f%s/s", speed, unit.c_str());
	char fspeed[20]="";
	sprintf(fspeed, "%.2f%s/s", speed, unit.c_str());
	if (!curlDown || curlDown->mFileLenth == 0 || nowDownloaded == 0) return 0;
	double nowDown = (curlDown->mFileLenth - totalToDownload + nowDownloaded);
	double curpercent = nowDown / curlDown->mFileLenth * 100; 
	curlDown->onProgress(curpercent, getSize(curlDown->mFileLenth), getSize(curlDown->mFileLenth - totalToDownload + nowDownloaded), fspeed);
	log("nowDd:%d; totalDown:%d; downProgress:%.2f%%",(int)(curlDown->mFileLenth - totalToDownload + nowDownloaded),(int)curlDown->mFileLenth , curpercent);
		
	return 0;
}




void CurlDown::downStart()
{
	thread _st_d(&CurlDown::downloadControler, this);//创建一个分支线程
	_st_d.detach();
}

void CurlDown::downloadControler()
{
	/*mFileName = mDownloadUrl.substr(mDownloadUrl.rfind('/') + 1);*/
	mFileLenth = getDownloadFileLenth();
	log("file size================%f", mFileLenth);
	if (mFileLenth <= 0) {		
		return;
	}	
	bool ret = false;
	int timeout = 30;
	while (true) { // 循环下载 每30秒进行下载 避免断网情况
		ret = download(timeout); //直接下载
		log("----ret--------->%d",ret);
		if (ret) { //下载完成
			break;
		}
		Sleep(500); //每次下载间隔500ms
	}

	if (ret)
	{		
		onSuccess(ret, mFilePath);
	}
}

bool CurlDown::download(long timeout)
{
	FILE *fp = NULL;
	if (_access(mFilePath.c_str(), 0) == 0) { // 以二进制形式追加
		fp = fopen(mFilePath.c_str(), "ab+");
	}
	else { // 二进制写
		fp = fopen(mFilePath.c_str(), "wb");
	}

	if (fp == NULL) {// 如果文件初始化失败进行返回
		return false;
	}

	// 读取本地文件下载大小
	int use_resume = 0;
	long localFileLenth = getLocalFileLength(); //已经下载的大小
	if (localFileLenth>0)
	{
		use_resume = 1;
	}
	log("filePath:%s  ; leng:%ld", mFilePath.c_str(), localFileLenth);
	CURL *handle = curl_easy_init();
	libcurl = handle;
	curl_easy_setopt(handle, CURLOPT_URL, mDownloadUrl.c_str()); 
//	curl_easy_setopt(handle, CURLOPT_TIMEOUT, timeout);//设置下载时间,超过时间断开下载,测试模式下把这行注释可以无时间限制下载
//	curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, 0);//感觉没什么卵用,得看下
	curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, getcontentlengthfunc);
	curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_func);   //写文件回调方法
	curl_easy_setopt(handle, CURLOPT_WRITEDATA, fp); // 写入文件对象
    curl_easy_setopt(handle, CURLOPT_RESUME_FROM, use_resume ? localFileLenth : 0);  // 从本地大小位置进行请求数据
//	curl_easy_setopt(handle, CURLOPT_RESUME_FROM_LARGE, (long long)(use_resume ? localFileLenth : 0)); // CURLOPT_RESUME_FROM_LARGE针对于大文件
	curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
	curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, progress_func); //下载进度回调方法
	curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, this); // 传入本类对象
	//curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, handle);//将handle传入

	CURLcode res = curl_easy_perform(handle);
	fclose(fp);

	return res == CURLE_OK;
}

long CurlDown::getDownloadFileLenth()
{
	CURL *handle = curl_easy_init();
	curl_easy_setopt(handle, CURLOPT_URL, mDownloadUrl.c_str());
	//curl_easy_setopt(handle, CURLOPT_HEADER, 1); //这个使libcurl会默认设置获取方式为HEAD方式,如果把set nobody的option去掉,又会下载文件内容!不可取
	curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "GET"); //设置为get方式获取
	curl_easy_setopt(handle, CURLOPT_NOBODY, 1);
	CURLcode retcCode = curl_easy_perform(handle);
	//查看是否有出错信息  
	const char* pError = curl_easy_strerror(retcCode);
	log("error===========================%s",pError);
	//清理curl,和前面的初始化匹配  
	curl_easy_cleanup(handle);
	if (retcCode == CURLE_OK) {
		curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &mFileLenth);
		log("filesize : %0.0f bytes\n", mFileLenth);
	}else {
		mFileLenth = -1;
	}
	return mFileLenth;
}

long CurlDown::getLocalFileLength()
{
	FILE *fp = fopen(mFilePath.c_str(), "r");
	fseek(fp, 0, SEEK_END);
	long length = ftell(fp);
	fclose(fp);
	return length;
}


代码已上传:

git下载地址  https://github.com/iuoon/TestCurl

csdn下载地址  http://download.csdn.net/detail/iuoon/9613925







  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值