在 cocos2d-x 中使用 libcurl 实现上传文件功能(附quick-cocos2d-x封装)
在 cocos2d-x 中使用 libcurl 实现上传文件功能(附quick-cocos2d-x封装)
Upload a file use libcurl in cocos2d-x.
本文基于 cocos2d-x 2.2.2 和 quick-cocos2d-x zrong修改版 3be9b8
目前做的项目中需要实现b截图分享功能,我的设计思路是使用 CCRenderTexture 来截图,并通过HTTP上传到截图分享服务器。
通过查看 cocos2d-x 源码,我发现 cocos2d-x 封装了一个 CCHttpClient 类,用于调用 libcurl 实现HTTP协议通信。不过并没有实现文件上传功能。
但 quick-cocos2d-x 不知道什么原因,删除了这个 CHttpClient 实现,而改用了 CCHTTPRequest 类,内部封装的依然是 libcurl ,但也依然没有实现文件上传功能。
既然都是封装 libcurl ,我们完全完全为现有的类扩展上传文件功能。在本文中,我基于 quick-cocos2d-x 的 CCHTTPRequest 类进行了扩展。cocos2d-x 如果需要扩展,方法也类似。
如果不愿意了解具体的封装过程,请直接clone我封装好的版本:quick-cocos2d-x forked by zrong
cURL 的文档
学习 cURL 最好的文档自然在 cURL 的官网 ,这里能找到所有的官方文档以及推荐,这些文档都写得不错。
如果来不及想看API,可以直接戳这里:Using the libcurl C Interface 。
直接看源码则是最快的上手方式:libcurl - small example snippets 。
php 中的 cURL 函数 也是不错的学习材料。优点就是有中文资源,缺点就是一些地方和标准 libcurl 库名称和用法不太相同。
表单上传用法
如果单纯使用 libcurl 来上传,那么官方的sample中就提供了具体的用法。戳这里:postit2.c。
因为有了上面的sample,我这里也就不再画蛇添足地演示整个流程了。我只说自认为重要(意思就是我犯过错)的地方吧。
在这个sample中,我们采用的是表单上传,因此采用了 curl_formadd
方法来构建表单。
我的表单是这样的:
1<form method="post" enctype="multipart/form-data">
2 <input id="filepath" name="filepath" type="file" />
3 <input type="submit" value="upload" />
4 <input name="act" type="hidden" value="upload" />
5</form>
在这个表单中,我一共给出了2个变量:
- filepath 指定要上传的文件;
- act 是个要传递的变量,值是 upload。
相应的,我的封装是这样的(省略了curl的初始化和变量定义,省略了curl的调用和资源的销毁。具体的流程看上面提到的 postit2.c):
1void CCHTTPRequest::setUploadFile(const char *filepath)
2{
3 curl_formadd(&m_formPost, &m_lastPost,
4 CURLFORM_COPYNAME, "filepath",
5 CURLFORM_FILE, filepath,
6 CURLFORM_CONTENTTYPE, "Image/jpeg",
7 CURLFORM_END);
8 curl_formadd(&m_formPost, &m_lastPost,
9 CURLFORM_COPYNAME, "act",
10 CURLFORM_COPYCONTENTS, "upload",
11 CURLFORM_END);
12 curl_easy_setopt(m_curl, CURLOPT_HTTPPOST, m_formPost);
13}
简单点说,我需要把表单中使用到的所有变量通过 curl_formadd
命令添加到表单中,然后使用 curl_easy_setopt
将表单内容传递给curl。
这两个API的说明:
在使用 CURLFORM_FILE
来传递值的时候,可以一起再设定一个 CURLFORM_CONTENTTYPE
指定上传的文件类型(Content-Type/Mime-Type)。常见的取值可以在这里找到: Content-Type/Mime-Type 。
扩展 CCHTTPRequest
根据上面的介绍,我对 quick-cocos2d-x 中包含的 CCHTTPRequest 进行了扩展,使其可以支持上传文件。
在 CCHTTPRequest.h 文件中,定义两个成员和两个方法:
1//......
2private:
3 curl_httppost *m_formPost;
4 curl_httppost *m_lastPost;
5public:
6 //用于设置上传文件名以及文件类型
7 void addFormFile(const char *name, const char *filePath, const char *fileType="application/octet-stream");
8 //用于增加附加的表单变量
9 void addFormContents(const char *name, const char *value);
10//......
在 CCHTTPRequest.cpp 文件中,实现着这个方法:
1void CCHTTPRequest::addFormFile(const char *name, const char *filePath, const char *contentType)
2{
3 curl_formadd(&m_formPost, &m_lastPost,
4 CURLFORM_COPYNAME, name,
5 CURLFORM_FILE, filePath,
6 CURLFORM_CONTENTTYPE, contentType,
7 CURLFORM_END);
8}
9
10void CCHTTPRequest::addFormContents(const char *name, const char *value)
11{
12 curl_formadd(&m_formPost, &m_lastPost,
13 CURLFORM_COPYNAME, name,
14 CURLFORM_COPYCONTENTS, value,
15 CURLFORM_END);
16}
找到 onRequest 方法的定义,在 curl_easy_perform
之前,加入这句代码:
1if (m_formPost)
2{
3 curl_easy_setopt(m_curl, CURLOPT_HTTPPOST, m_formPost);
4}
还是在 onRequest 方法中,在 curl_easy_clearup
之后,加入这句代码:
1if (m_formPost)
2{
3 curl_formfree(m_formPost);
4 m_formPost = NULL;
5}
导出到 lua
CCHTTPRequest 并没有单独的tolua文件。所有位于 lib/cocos2d-x/external/extra
包中的功能的导出都集中在 lib/cocos2d-x/external/extra/luabinding/cocos2dx_extra_luabinding.tolua
中。关于这点,我足足找了半小时,多亏 ChildhoodAndy 提醒,再次感谢。
在该文件中相应的地方增加这两个要导出的方法,然后运行 lib/cocos2d-x/external/extra/luabinding/build.bat
,并重新编译 quick-x-player 即可实现导出。
完善 network.lua
我在 network.lua 中增加了一个 uploadFile 方法,这样用起来更加方便。
该方法定义如下:
1-- @author zrong(zengrong.net)
2-- Creation: 2014-04-14
3-- @param callback 和 network.createHTTPRequest 的第一个参数一样。
4-- @param url 和 network.createHTTPRequest 的第二个参数一样。
5-- @param datas 包含下列值:
6-- fileFiledName(表单中的type值为file的input标签的name值);
7-- filePath(要上传文件的绝对路径)
8-- contentType(可选,上传文件的 Content-Type。默认的值为 application/octet-stream)
9-- extra(可选,要附加到form中的变量的键值对)
10function network.uploadFile(callback, url, datas)
11 assert(datas or datas.fileFieldName or datas.filePath, "Need file datas!")
12 local request = network.createHTTPRequest(callback, url, "POST")
13 local fileFieldName = datas.fileFieldName
14 local filePath = datas.filePath
15 local contentType = datas.contentType
16 if contentType then
17 request:addFormFile(fileFieldName, filePath, contentType)
18 else
19 request:addFormFile(fileFieldName, filePath)
20 end
21 if datas.extra then
22 for i in ipairs(datas.extra) do
23 local data = datas.extra[i]
24 request:addFormContents(data[1], data[2])
25 end
26 end
27 request:start()
28 return request
29end
使用范例:
1network.uploadFile(function(evt)
2 if evt.name == "completed" then
3 local request = evt.request
4 printf("REQUEST getResponseStatusCode() = %d", request:getResponseStatusCode())
5 printf("REQUEST getResponseHeadersString() =\n%s", request:getResponseHeadersString())
6 printf("REQUEST getResponseDataLength() = %d", request:getResponseDataLength())
7 printf("REQUEST getResponseString() =\n%s", request:getResponseString())
8 end
9 end,
10 "http://127.0.0.1/upload.php",
11 {
12 fileFieldName="filepath",
13 filePath=device.writablePath.."screen.jpg",
14 contentType="Image/jpeg",
15 extra={
16 {"act", "upload"},
17 }
18 }
19)
已经封装好的版本,请查看: quick-cocos2d-x forked by zrong
- 文章ID:2088
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/upload-a-file-in-quick-cocos2d-x/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。