cocos2d-x jsbinding 资源下载实现
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');
}
}

浙公网安备 33010602011771号