cocos2dx没有直接给出资源下载的api,可能是因为资源的管理每个项目的需求不太一样,所以完整的资源下载功能需要我们自己去实现。
资源下载分为两部分,一部分是资源请求,另一部分是资源文件写入。资源请求模块,cocos2d-x封装了curl的功能,主要实现是extensions\network下的几个类,通过他们我们可以方便的实现Http请求的功能。资源的写入主要是利用fwrite函数将数据流写入文件。完成了C++模块的实现以后,我们要做的是绑定到js,这样我们就可以在js端发起请求,将资源下载到手机。这里C++绑定还有Http请求的封装主要借鉴了https://github.com/akira-cn/cocos2dx-cqwrap/blob/master/cqwrap/src/scripting/cqwrap_httprequest_manual.cpp 我在此的基础上做了一些改动,同时增加了文件的写入功能
#include "cqwrap_register_all_manual.h" #include "util/JsonHelper.h" #include "pattern/EventProxy.h" #include <algorithm> #include <sstream> #include "cocos-ext.h" // 这个用于获取各个平台的资源缓存缓存路径,关于缓存路径,请看http://www.cnblogs.com/hzd822/p/3258641.html #include "KT_ALL_PLATFORMS.h" USING_NS_CC_EXT; void js_register_cocos2dx_extension_httprequest(JSContext *cx, JSObject *global); JSClass *jsb_HttpRequest_Class; JSObject *jsb_HttpRequest_prototype; void register_cqwrap_httprequest(JSContext* cx, JSObject* obj) { // first, try to get the ns jsval nsval; JSObject *ns; JS_GetProperty(cx, obj, "cc", &nsval); if (nsval == JSVAL_VOID) { ns = JS_NewObject(cx, NULL, NULL, NULL); nsval = OBJECT_TO_JSVAL(ns); JS_SetProperty(cx, obj, "cc", &nsval); } else { JS_ValueToObject(cx, nsval, &ns); } obj = ns; js_register_cocos2dx_extension_httprequest(cx, obj); } // 这个类对CCHttpRequest的封装 class HttpRequest: public CCObject{ protected: CCHttpRequest* m_request; std::vector<std::string> m_headers; int m_writefile; std::string m_url; struct xorStruct { xorStruct(char value) : m_value(value) {} char m_value; char operator()(char in) const { return in ^ m_value; } }; void responseCallback(cocos2d::CCNode *sender, void *data){ CCHttpResponse *response = (CCHttpResponse*)data; if (!response) { CCLog("no response..."); return; } int statusCode = response->getResponseCode(); char statusString[64] = {}; sprintf(statusString, "HTTP Status Code: %d", statusCode); CCLOG("response code: %d", statusCode); if (!response->isSucceed()) { CCLOG("response failed"); CCLOG("error buffer: %s", response->getErrorBuffer()); JsonData* msg = new JsonData(); (*msg)["data"] = response->getErrorBuffer(); PROXY_FIRE("error", msg); CC_SAFE_DELETE(msg); return; } JsonData* msg = new JsonData(); // dump data std::vector<char> *buffer = response->getResponseData(); if (m_writefile == 1){ // 获取相对路径 std::string relativePath = m_url.substr(m_urlhostNum); std::string filePath; filePath = kt_library_path(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) filePath = filePath + "/Caches/"; #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) filePath = filePath + "/Caches/"; #endif filePath.append(relativePath); this->writeToFile(filePath.c_str(), *buffer); (*msg)["status"] = statusCode; }else{ std::string buf(buffer->begin(),buffer->end()); (*msg)["data"] = buf.c_str(); } PROXY_FIRE("complete", msg); CC_SAFE_DELETE(msg); }; public: int m_urlhostNum; void send(const char* buffer = NULL){ if(m_request != NULL){ if(NULL != buffer){ m_request->setRequestData(buffer, sizeof buffer); } m_request->setHeaders(m_headers); CCHttpClient::getInstance()->send(m_request); CC_SAFE_RELEASE_NULL(m_request); m_headers.clear(); } }; // 文件写入 bool writeToFile(const char *filePath,std::vector<char> _responseData) { FILE* fout = fopen(filePath, "wb+"); if (fout == NULL) return false; if (_responseData.size() > 0) { if (fwrite(&_responseData[0], sizeof(char), _responseData.size(), fout) != _responseData.size()) { fclose(fout); return false; } } fclose(fout); return true; } void setRequestHeader(std::string key, std::string content){ if(m_request != NULL){ key += ": "; key += content; m_headers.push_back(key); } }; void setRequestData(std::string* requestData){ if(m_request != NULL){ m_request->setRequestData(requestData->c_str(),requestData->length()); } }; void open(CCHttpRequest::HttpRequestType type, const char* url,int writeFile){ CC_SAFE_RELEASE_NULL(m_request); m_writefile = writeFile; m_request = new CCHttpRequest(); m_request->setUrl(url); m_request->setRequestType(type); m_request->setResponseCallback(this, callfuncND_selector(HttpRequest::responseCallback)); m_url = url; }; HttpRequest(){ m_request = NULL; m_headers = std::vector<std::string>(); m_writefile = 0; m_urlhostNum = 32; }; ~HttpRequest(){ CC_SAFE_RELEASE_NULL(m_request); }; }; // 从这里开始是将C++类绑定到js,使js可以调用C++函数 JSBool js_cocos2dx_extension_HttpRequest_setRequestHeader(JSContext *cx, uint32_t argc, jsval *vp){ jsval *argv = JS_ARGV(cx, vp); JSObject *obj = JS_THIS_OBJECT(cx, vp); js_proxy_t *proxy; JS_GET_NATIVE_PROXY(proxy, obj); HttpRequest* cobj = (HttpRequest *)(proxy ? proxy->ptr : NULL); JSB_PRECONDITION2( cobj, cx, JS_FALSE, "Invalid Native Object"); if(argc == 2){ std::string* key = new std::string(); do { JSBool ok = jsval_to_std_string(cx, argv[0], key); JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error processing arguments"); } while (0); std::string* value = new std::string(); do { JSBool ok = jsval_to_std_string(cx, argv[1], value); JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error processing arguments"); } while (0); cobj->setRequestHeader(*key, *value); JS_SET_RVAL(cx, vp, JSVAL_VOID); CC_SAFE_DELETE(key); CC_SAFE_DELETE(value); return JS_TRUE; } JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 1); return JS_FALSE; } JSBool js_cocos2dx_extension_HttpRequest_setRequestData(JSContext *cx, uint32_t argc, jsval *vp){ jsval *argv = JS_ARGV(cx, vp); JSObject *obj = JS_THIS_OBJECT(cx, vp); js_proxy_t *proxy; JS_GET_NATIVE_PROXY(proxy, obj); HttpRequest* cobj = (HttpRequest *)(proxy ? proxy->ptr : NULL); JSB_PRECONDITION2( cobj, cx, JS_FALSE, "Invalid Native Object"); if(argc == 1){ std::string* requestData = new std::string(); do { JSBool ok = jsval_to_std_string(cx, argv[0], requestData); JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error processing arguments"); } while (0); cobj->setRequestData(requestData); JS_SET_RVAL(cx, vp, JSVAL_VOID); CC_SAFE_DELETE(requestData); return JS_TRUE; } JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 0); return JS_FALSE; } JSBool js_cocos2dx_extension_HttpRequest_open(JSContext *cx, uint32_t argc, jsval *vp){ jsval *argv = JS_ARGV(cx, vp); JSObject *obj = JS_THIS_OBJECT(cx, vp); js_proxy_t *proxy; JS_GET_NATIVE_PROXY(proxy, obj); HttpRequest* cobj = (HttpRequest *)(proxy ? proxy->ptr : NULL); JSB_PRECONDITION2( cobj, cx, JS_FALSE, "Invalid Native Object"); if(argc == 2 || argc == 3 || argc == 4){ std::string* method = new std::string(); do { JSBool ok = jsval_to_std_string(cx, argv[0], method); JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error processing arguments"); } while (0); std::string* url = new std::string(); do { JSBool ok = jsval_to_std_string(cx, argv[1], url); JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error processing arguments"); } while (0); int writeFile = 1; if (argc == 3){ do { JSBool ok = jsval_to_int32(cx, argv[2], &writeFile); JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error processing arguments"); } while (0); } int urlhostNum; if (argc == 4){ do { JSBool ok = jsval_to_int32(cx, argv[3], &urlhostNum); JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error processing arguments"); cobj->m_urlhostNum = urlhostNum; } while (0); } if(*method == "POST"){ cobj->open(CCHttpRequest::kHttpPost, url->c_str(),writeFile); }else{ cobj->open(CCHttpRequest::kHttpGet, url->c_str(),writeFile); } JS_SET_RVAL(cx, vp, JSVAL_VOID); CC_SAFE_DELETE(url); CC_SAFE_DELETE(method); return JS_TRUE; } JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 1); return JS_FALSE; } JSBool js_cocos2dx_extension_HttpRequest_send(JSContext *cx, uint32_t argc, jsval *vp){ jsval *argv = JS_ARGV(cx, vp); JSObject *obj = JS_THIS_OBJECT(cx, vp); js_proxy_t *proxy; JS_GET_NATIVE_PROXY(proxy, obj); HttpRequest* cobj = (HttpRequest *)(proxy ? proxy->ptr : NULL); JSB_PRECONDITION2( cobj, cx, JS_FALSE, "Invalid Native Object"); if(argc == 1){ std::string* data = new std::string(); do { JSBool ok = jsval_to_std_string(cx, argv[0], data); JSB_PRECONDITION2( ok, cx, JS_FALSE, "Error processing arguments"); } while (0); cobj->send(data->c_str()); JS_SET_RVAL(cx, vp, JSVAL_VOID); CC_SAFE_DELETE(data); return JS_TRUE; } if(argc == 0){ cobj->send(); JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 1); return JS_FALSE; } JSBool js_cocos2dx_extension_HttpRequest_oncomplete(JSContext *cx, uint32_t argc, jsval *vp){ JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_FALSE; } JSBool js_cocos2dx_extension_HttpRequest_onerror(JSContext *cx, uint32_t argc, jsval *vp){ JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } void js_cocos2dx_extension_HttpRequest_finalize(JSFreeOp *fop, JSObject *obj){ } JSBool js_cocos2dx_extension_HttpRequest_constructor(JSContext *cx, uint32_t argc, jsval *vp){ if(argc == 0){ HttpRequest* cobj = new HttpRequest(); cocos2d::CCObject *_ccobj = dynamic_cast<cocos2d::CCObject *>(cobj); if (_ccobj) { _ccobj->autorelease(); } TypeTest<cocos2d::extension::CCHttpRequest> t; js_type_class_t *typeClass; uint32_t typeId = t.s_id(); HASH_FIND_INT(_js_global_type_ht, &typeId, typeClass); assert(typeClass); JSObject *obj = JS_NewObject(cx, typeClass->jsclass, typeClass->proto, typeClass->parentProto); JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); // link the native object with the javascript object js_proxy_t *p; JS_NEW_PROXY(p, cobj, obj); JS_AddNamedObjectRoot(cx, &p->obj, "HttpRequest"); return JS_TRUE; } JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 0); return JS_FALSE; } void js_register_cocos2dx_extension_httprequest(JSContext *cx, JSObject *global) { jsb_HttpRequest_Class = (JSClass *)calloc(1, sizeof(JSClass)); jsb_HttpRequest_Class->name = "HttpRequest"; jsb_HttpRequest_Class->addProperty = JS_PropertyStub; jsb_HttpRequest_Class->delProperty = JS_PropertyStub; jsb_HttpRequest_Class->getProperty = JS_PropertyStub; jsb_HttpRequest_Class->setProperty = JS_StrictPropertyStub; jsb_HttpRequest_Class->enumerate = JS_EnumerateStub; jsb_HttpRequest_Class->resolve = JS_ResolveStub; jsb_HttpRequest_Class->convert = JS_ConvertStub; jsb_HttpRequest_Class->finalize = js_cocos2dx_extension_HttpRequest_finalize; jsb_HttpRequest_Class->flags = JSCLASS_HAS_RESERVED_SLOTS(2); static JSPropertySpec properties[] = { {0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER} }; static JSFunctionSpec funcs[] = { JS_FN("oncomplete",js_cocos2dx_extension_HttpRequest_oncomplete, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FN("onerror",js_cocos2dx_extension_HttpRequest_onerror, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FN("send",js_cocos2dx_extension_HttpRequest_send, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FN("open",js_cocos2dx_extension_HttpRequest_open, 2, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FN("setRequestHeader",js_cocos2dx_extension_HttpRequest_setRequestHeader, 2, JSPROP_PERMANENT | JSPROP_ENUMERATE), JS_FS_END }; jsb_HttpRequest_prototype = JS_InitClass( cx, global, jsb_HttpRequest_prototype, jsb_HttpRequest_Class, js_cocos2dx_extension_HttpRequest_constructor, 0, // constructor properties, funcs, NULL, // no static properties NULL); // make the class enumerable in the registered namespace JSBool found; JS_SetPropertyAttributes(cx, global, "HttpRequest", JSPROP_ENUMERATE | JSPROP_READONLY, &found); // add the proto and JSClass to the type->js info hash table TypeTest<cocos2d::extension::CCHttpRequest> t; js_type_class_t *p; uint32_t typeId = t.s_id(); HASH_FIND_INT(_js_global_type_ht, &typeId, p); if (!p) { p = (js_type_class_t *)malloc(sizeof(js_type_class_t)); p->type = typeId; p->jsclass = jsb_HttpRequest_Class; p->proto = jsb_HttpRequest_prototype; p->parentProto = NULL; HASH_ADD_INT(_js_global_type_ht, type, p); } }
绑定完之后,js的调用就很简单了
// 创建http请求,请求远程图片 var httpRequest = cc.HttpRequest(); httpRequest.open('GET',fullPath); httpRequest.send(); httpRequest.oncomplete = function (evt){ if (evt.status == 200){ cc.log(fullPath+'------downLoad success'); // 下载成功 } } httpRequest.onerror = function (evt){ cc.log(fullPath+'------downLoad error'); } }