- 云开发 security.imgSecCheck 调用响应错误
使用云函数调用 security.imgSecCheck ,对图片进行鉴黄请求响应为: [代码]{[代码] [代码]errMsg: [代码][代码]"cloud.callFunction:ok",[代码] [代码]result:{[代码][代码] errCode: 41005[代码][代码] errMsg: [代码][代码]"openapi.security.imgSecCheck:fail media data missing hint: [9ZGoCA02628622]" [代码] [代码] }[代码] [代码]}[代码] 云函数代码如下: [代码]const cloud = require([代码][代码]'wx-server-sdk'[代码][代码])[代码] [代码]cloud.init();[代码] [代码]// 云函数入口函数[代码][代码]exports.main = (event) => {[代码][代码] [代码][代码]console.log(event);[代码][代码] [代码][代码]return[代码] [代码]cloud.openapi.security[代码][代码] [代码][代码].imgSecCheck({[代码][代码] [代码][代码]media: {[代码][代码] [代码][代码]contentType: [代码][代码]'image/png'[代码][代码],[代码][代码] [代码][代码]value: event.img[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] [代码][代码].then(result => {[代码][代码] [代码][代码]return[代码] [代码]result;[代码][代码] [代码][代码]})[代码][代码] [代码][代码].[代码][代码]catch[代码][代码](err => {[代码][代码] [代码][代码]return[代码] [代码]err;[代码][代码] [代码][代码]})[代码][代码]}[代码] 调用代码如下: [代码]uploadImg: [代码][代码]function[代码][代码]() {[代码][代码] [代码][代码]this[代码][代码].selectImg().then(img => {[代码][代码] [代码][代码]console.log(img);[代码][代码] [代码][代码]return[代码] [代码]this[代码][代码].imgSecCheck(img);[代码][代码] [代码][代码]}).then(res => {[代码][代码] [代码][代码]console.log([代码][代码]"success:"[代码][代码], res);[代码][代码] [代码][代码]}).[代码][代码]catch[代码][代码](err => {[代码][代码] [代码][代码]console.log([代码][代码]"fail"[代码][代码], err);[代码][代码] [代码][代码]})[代码][代码] [代码][代码]},[代码] // 选择图片并转为 buffer [代码] [代码][代码]selectImg: [代码][代码]function[代码][代码]() {[代码][代码] [代码][代码]return[代码] [代码]new[代码] [代码]Promise((resolve, reject) => {[代码][代码] [代码][代码]wx.chooseImage({[代码][代码] [代码][代码]count: 1,[代码][代码] [代码][代码]sizeType: [[代码][代码]'original'[代码][代码], [代码][代码]'compressed'[代码][代码]],[代码][代码] [代码][代码]success: [代码][代码]function[代码][代码](res) {[代码][代码] [代码][代码]let params = {[代码][代码] [代码][代码]filePath: res.tempFilePaths[0][代码][代码] [代码][代码]};[代码][代码] [代码][代码]wx.getFileSystemManager()[代码][代码] [代码][代码].readFile({[代码][代码] [代码][代码]filePath: res.tempFilePaths[0],[代码][代码] [代码][代码]success: res => {[代码][代码] [代码][代码]console.log([代码][代码]"readSuccess:"[代码][代码], res);[代码][代码] [代码][代码]resolve(res.data);[代码][代码] [代码][代码]},[代码][代码] [代码][代码]fail: err => {[代码][代码] [代码][代码]console.log([代码][代码]"readFail:"[代码][代码], err);[代码][代码] [代码][代码]reject(err);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]});[代码] [代码] [代码][代码]},[代码][代码] [代码][代码]})[代码][代码] [代码][代码]})[代码][代码] [代码][代码]},[代码] // 调用云函数[代码] [代码][代码]imgSecCheck: [代码][代码]function[代码][代码](img) {[代码][代码] [代码][代码]return[代码] [代码]wx.cloud.callFunction({[代码][代码] [代码][代码]name: [代码][代码]"imgSecCheck"[代码][代码],[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]img: img[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] [代码][代码]},[代码]
2019-07-18 - 如何不用服务器来开发一个小游戏
作者:代长新 我是代长新,来自上海享物说,主要负责游戏客户端研发。 [图片] 享物说是一个大家可以互相赠送物品,有趣、不花钱的社区平台。为了创造更好的社区氛围,我们决定通过小游戏来增加社区的趣味性和互动性。 《乐享花园》是我们在小游戏领域的第一个实践。这个游戏,从立项到做完,准确的说是客户端做完,我们一共用了3天的时间。 但是,当时我们种花浇花、领水滴任务都是通过浏览器缓存实现的,如果要上线还要等服务端人员到位,否则玩家清理一下手机,自己种的花就没了。但等我们服务端人员到位,再到游戏上线,就是几周以后的事情了。 小游戏开发之痛:无法摆脱对服务端的依赖 相信,这也是大部分小游戏开发时会遇到的问题 —— 功能很简单,但就是摆脱不了对服务端的依赖。如下图: [图片] 具体来说,小游戏对服务端的依赖主要有以下两个方面: 1、微信接口只支持在服务端调用 这就意味着,我们必须为这些接口架设一个中转服务器。如果没有这个中转服务器,我们就没法做用户登录,没法获取用户头像、名称信息,也拿不到access_token,更没有办法调用其他微信接口,如内容审查。 2、游戏功能实现需要服务器开发 对于很多小游戏来说,我们唯一用到服务端的地方就是,储存当前的关卡进度,展示一个世界排行,就可以了。而想要实现这么一个简单的需求时,你会发现,隔行如隔山。 如何用云开发解决小游戏在服务端痛点? 1、云函数实现微信接口调用 曾经,我想过绕开服务器,直接通过客户端请求微信接口,结果踩了一个坑。 当时做的是聊天功能,需要对玩家发送的消息进行内容审查。我看完了接口文档,就跑过去跟服务器同学说,内容审查我这边全部做掉就可以,他那边不需要做额外的处理。 [图片] 等我们调通,上了体验版,一打开报错,我才想起来,这个接口文档的上面,有一行小字,而且颜色是灰色的,上面写着:此接口应在后端服务器调用。 第一次看到这句话,还以为它只不过是一个警告,所以根本没把它放在心上,哪知道它居然是一个error!而在这之前,我还特意做了一些我认为比较人性化的设计,比如使用这个接口需要一个密钥,这个密钥是有有效期的,当密钥过期的时候,我会把玩家发送的内容保存起来,向后端拉取新的密钥后,再发送出去,这样对于玩家来说,整个过程是无感知的。而现在则意味着所有这些都要服务器去实现了。 后来,我通过云开发来实现多有接口调用,事情就简单多了。 [图片] 就拿登录来说吧。 由于云函数具有微信天然鉴权的能力,可以直接返回openid,这一点对做登录确实很方便。乐享花园需要和享物说平台打通小红花积分数据,所以需要用户的unionid信息,这一步也是在云函数中实现的。 还有access_token,就是刚才用到的密钥,**为什么要单独说这个密钥呢?因为它会用到云函数特别有意思的功能,那就是定时触发器。**由于这个密钥是有两个小时有效期的,我们设定一个小时间隔定时刷新,保存到数据库中,用的时候直接从数据库中取出来就可以了,这样可以保证密钥永远是不过期的。 [图片] 通过云开发,为微信接口准备的中转服务器就不需要了;更重要的是,**服务端与微信接口分离,无需关心客户端场景。**不管这个客户端,是来自h5游戏,还是来自小游戏环境,对于服务端来说,都是一样的,再也不需要为客户端提供这样那样的权限接口。 2、云函数+数据库,实现全局排行榜功能 正如前面提到的痛点,小游戏开发对服务端的另一个依赖是游戏功能的实现。对于大部分小游戏来说,我们唯一用到服务端的地方就是:保存用户数据,展示一个世界排行榜。而如果用传统服务器实现这些功能的话,你会发现需要了解的后端架构知识非常庞大。 有次,我到服务端同学的旁边,原本是打算diss他的,因为我功能已经写完了,他还不知道在忙些什么东西。这时我看到他在做什么呢 —— 一边写dockfile文件,一边写linux命令,一边打开Postman调试,完了后发邮件给运维说要执行几个mysql语句。 而所有这些都还没有涉及到他要开发的游戏功能! 所以说,一门后端语言从会写,到可以放到生产环境中,是两个完全不一样的概念。 云开发提供了数据库、云函数、云存储,通过这些能力,我们完全可以取代服务器来实现游戏功能。 在《乐享花园》里,我们通过云开发实现了全民成语接龙这个游戏功能,并且只用了2个云函数就实现了我们对服务器的全部需求。这里简单介绍一下这两个云函数: **第一个云函数是用来展示世界排行榜。**由于云函数拉取数据库的条目是有限制的,最大是100条,其实这个已经足够满足需求了;当然了,你要说我们的客户端很牛,性能不是问题,数据什么的先给我来个2000条,也不是不可以,这里做个处理就可以了。 另外在检索数据库数据时,这个过程会很慢,一定要记得,在后台添加数据库索引,可以把这个过程理解为通过磁盘换取CPU计算。这样速度会快很多。 [图片] **第二个云函数是用来上报玩家数据。**这个比较简单,一行代码搞定。 [图片] 就这样从微信接口调用,到游戏功能开发,一款不需要服务器的小游戏就全部开发完成了。 小结 其实,云开发可以使用的业务场景,还有很多,比如, 绕过微信https域名请求限制 存放游戏的全局设置 保存玩家的个性化数据 。。。 作为开发者,也希望云开发未来,可以提供更多的业务场景支持,比如 websocket,刚才说的聊天服务器,就可以省掉了; 帧同步,实时对战类游戏的实现,就不再有压力; 日志服务,方便统计,和排查玩家的行为,方便游戏迭代优化; 大数据统计分析,可以做一些事件漏斗等等~ 这样小游戏的研发门槛,就降得很低很低了!
2019-03-06 - 云开发实战分享|随手记Lite小程序
作者:锋少 一、做一款轻便的备忘录小工具 有次在外地出差开会,台上的演讲者妙语连珠,分享的颇具启发性,我想把这些分享都记录下来,但一时找不到合适的记录地方,于是和身边大多数人一样,打开微信在 [代码]文件传输助手[代码]或 [代码]亲人的微信[代码]开始记录。但是这种形式的记录没有形成一个完整的记录体系,且时间久了很难再找到。 会后我在软件市场找了一圈,都没有找到特别合适的软件应用:一方面它们功能太繁琐复杂,远超我的需求;另一方面像这种不是特别高频使用的 [代码]APP[代码],说实话,我真不太愿意专门为此下载安装。 于是我想到小程序 —— [代码]轻量[代码]、 [代码]便捷[代码]、 [代码]即开即用[代码],用小程序开发这么一款这样做备忘录的小工具,非常合适。 二、主要功能 创建备忘录:内容快速记录,支持表情和图片,自动获取标题和时间,可选择记录位置 备忘录查询:历史记录按时间排序,允许记录回查,导航到记录位置 备忘录修改:允许重复编辑修改 三、随手记Lite功能实现 3.1、准备工作 1、注册微信小程序账号: 方式一:直接注册(https://mp.weixin.qq.com/wxopen/waregister?action=step1) 方式二:已经有微信公众号(已认证)朋友可以直接【登录公众号】 -> 【小程序管理】 -> 【添加】->【快速注册并认证小程序】 注册完成后,找到小程序的 [代码]AppID[代码]和 [代码]AppSecret[代码] [图片] 2、下载微信开发者工具、创建项目 ,打开开发者工具,键入项目目录、项目名称、刚才的 [代码]AppID[代码],此时项目创建成功,然后点击开发者工具上方的【云开发】开通云开发。小程序·云开发官方地址(https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/getting-started.html) 3.2功能实现一: 创建备忘录(用户文本及图片内容的上传存储) 1)功能简要描述:对于随手记Lite来说,为用户提供文本及图片上传,快速记录,是最基本的功能 2)从用户端到云开发的信息处理流程图及描述 [图片] 如果记录中包含图片,则先上传图片,将返回的 [代码]fileID[代码]添加到数组,最后将数组和记录内容上传到云数据库;如果记录中不包含图片则直接将记录内容上传到云数据库。 3)功能核心代码 [代码]// 用户填写信息后,提交事件 fromSubmit: function(e) { let submitInfo = e.detail.value let categoryId = Number(submitInfo.categoryIndex) var title = submitInfo.title ? submitInfo.title : this.data.datas.title2 var images = []; for (vari = 0; i < this.data.uploadImages.length; i++) { let randString = Math.floor(Math.random() * 1000000).toString() + '.png' wx.cloud.uploadFile({ // 以昵称创建云存储文件夹路径 cloudPath: wx.getStorageSync("wxUserInfo").nickName + '/' + Date.now() + '.png', filePath: this.data.uploadImages[i], success: res => { images = images.concat(res.fileID) // 图片全部上传完毕,上传记录数据 if (images.length == this.data.uploadImages.length) { db.collection('allnote') .add({ data: { "titlename": title, "createtime": util.formatTime2(new Date()), "updatetime": null, "address": submitInfo.locationName ? submitInfo.locationName : "未选择位置", "latAndLong": this.data.datas.latAndLong, "content": submitInfo.content, "images": images, 'nickName': wx.getStorageSync("wxUserInfo").nickName, 'avatar': wx.getStorageSync("wxUserInfo").avatarUrl, 'province': wx.getStorageSync("wxUserInfo").province, } }) .then(res => { wx.hideLoading() wx.navigateBack({}) }) } }, fail: console.error }) } } }, [代码] 3.2 功能实现二:记录查询功能 1)功能简要描述:已经创建备忘录的用户,可以按照时间顺序查看所有记录及任意记录的详细内容 2)从用户端到云开发的信息处理流程图及描述 [图片] 小程序端用户上传自己的 [代码]userId[代码]到云函数,云函数根据该 [代码]userId[代码]到云数据库请求对应数据,数据返回到云函数后,云函数进行排序和时间格式截取处理,最后将数据返回到小程序端。 3)功能核心代码 [代码]<!--云函数端--> // 云函数入口函数 exports.main = async(event, context) => { return await new Promise(function(resolve, reject) { var notes = [] var notesNew = [] db.collection('allnote').where({ _openid: event.userOpenid }).get().then(res => { console.log(res) notes = res.data if (res.data) { // 由于时间格式问题,orderBy('createtime', 'desc')排序无效,所以使用以下排序方式 // 按照时间倒叙排列 notes.sort(function(a, b) { return Date.parse(b.createtime) - Date.parse(a.createtime); }); // 显示的时间格式转换,截取秒 for (var i = 0; i < res.data.length; i++) { let showTime = res.data[i].createtime.substring(0, res.data[i].createtime.length - 3) notes[i].showCreateTime = showTime // 数据解码 notes[i].titlename = decodeURI(notes[i].titlename) // 将加入新格式的时间放入新的数组 notesNew.push(notes[i]) } // 返回新的数组 resolve(notesNew) } }).catch(error => { reject(error) }) }) } <!--小程序端--> // 请求心得数据 requestNoteData: function() { let _this = this; wx.cloud.callFunction({ name: 'getMyNote', data: { userOpenid: wx.getStorageSync('userId'), skip:0 } }).then(res => { console.log(res) _this.setData({ notes: res.result }) }).catch(err => { console.log(err) }) }, [代码] 3.3 功能实现三:备忘录修改功能 1)功能简要描述:对于记录详细内容,用户除了可以查看时间、位置和内容之外,还可以进行内容的编辑和删除记录 2)从用户端到云开发的信息处理流程图及描述 [图片] 用户修改记录时,直接上传新的记录内容和记录id调用 [代码].update[代码]进行即可。 3)功能核心代码 [代码]<!--记录详情页数据获取--> onLoad: function(options) { // 根基记录id获取记录详情 db.collection('allnote') .where({ _id: options._id }) .get() .then(res => { console.log(res) noteData = res.data[0] // 显示的时间格式转换,截取秒 let showTime = res.data[0].createtime.substring(0, res.data[0].createtime.length - 3) noteData.createtime = showTime // 数据解码,如果上传的时候没有encodeURI,则不需要decodeURI noteData.titlename = decodeURI(noteData.titlename) noteData.content = decodeURI(noteData.content) _this.setData({ noteData: noteData }) wx.hideLoading() }) }, <!--更新单条记录--> // 单条记录提交更新 fromSubmit: function(e) { let _this = this; var submitInfo = e.detail.value db.collection('allnote').doc(_this.data.noteData._id).update({ data: { titlename: submitInfo.name, content: submitInfo.content } }) .then(res => { wx.navigateBack({ }) }) .catch(console.error) }, [代码] 四、小结 其实一开始,我的随手记LIte小程序是计划用小程序+WEB后台开发实现的,从买域名(备案···)到后台开发环境搭建占到了这个项目的2/3的时间,且域名、服务器、CA证书都需要管理续费。 有一天我收到公众号推送小程序·云开发的消息,打开一看太惊喜了,用1天时间了解学习了下,第二天我便将后台切换到了云开发。 我可以将大部分精力都放在业务的实现上,可以说是零部署,零维护,也不需要操心域名和服务器相关的东西,而且云控制台的数据和文件都是可视化管理,用户登录寥寥几行代码就可实现,常规的数据读写都封装好了接口。 期待小程序·云开发开放出更多的接口和功能··· 五、项目预览 [图片]
2019-03-07