- 批量下载云存储中的文件
最近开始学习使用小程序和小程序的云开发功能,遇到了文件无法批量下载的问题,以下是个人摸索学习的一些经历,在此分享一下 本次为测试环境下的一次尝试,假设小程序已经开通了云开发功能(不开如何使用云存储- -|||) 使用的是官方的云开发默认模板创建的项目,GO! 先往云存储中上传了一些文件,文件结构如下 |-- folder1 |-- file1.txt |-- file2.txt |-- file3.txt |-- folder2 |-- file4.txt |-- file5.txt |-- folder3 |-- file6.txt [图片] 在'cloudfunctions'下新建一个Node.js云函数,在这里就随意了,实际可根据具体的需要来命名 [图片] 进入该函数所在的目录下启动终端,运行命令 // 1.先为了保险起见 npm install // 2.安装这个包,可参考 https://docs.cloudbase.net/api-reference/manager/node/introduction npm install @cloudbase/manager-node 安装完毕后在入口文件开始撸代码 const CloudBase = require('@cloudbase/manager-node') const { storage } = new CloudBase({ envId: "xxxxxx", // 云开发环境ID,进入云开发的时候可以从右上角进行复制 }); // 获取云存储的所有文件信息 async function listDirectoryFiles(params) { const { path } = params // 接口功能:列出文件夹下的所有文件 // 接口声明:listDirectoryFiles(cloudPath: string): Promise<IListFileInfo[]> return await storage.listDirectoryFiles(path); } // 获取云存储文件列表的临时下载链接 async function getTemporaryUrl(params) { const { fileList } = params let newFileList = fileList.filter(file => file.Size !== '0').map(file => ({ cloudPath: file.Key, maxAge: 86400 // 临时下载链接有效时间,单位:秒 })) // 接口功能:获取文件临时下载链接 // 接口声明:getTemporaryUrl(fileList: (string | TempUrlInfo)[]):Promise<Array.<FileUrlItem>> return await storage.getTemporaryUrl(newFileList); } // 云函数入口函数 exports.main = async (event, context) => { const { functionName, params } = event if (functionName === 'listDirectoryFiles') { return await listDirectoryFiles(params) } else if (functionName === 'getTemporaryUrl') { return await getTemporaryUrl(params) } } 为了调用这些代码,需要上传该云函数(或者在本地测试也可以),然后撸个测试页面 <!--pages/test/test.wxml--> <button type="primary" bindtap="getFileUrlList">获取文件url列表</button> // pages/test/test.js Page({ /** * 页面的初始数据 */ data: { }, getFileUrlList() { // 先调用获取文件列表的云函数 wx.cloud.callFunction({ name: 'cloudManagerFunctions', data: { functionName: 'listDirectoryFiles', params: { path: '/' } } }).then(res => { const { result } = res // 处理该文件列表,调用获取下载链接的云函数 return wx.cloud.callFunction({ name: 'cloudManagerFunctions', data: { functionName: 'getTemporaryUrl', params: { fileList: result } } }) }).then(res => { const { result } = res console.log(result) }) }, //......先忽略生命周期函数 }) 然后点击Button触发事件,可以看到控制台打印了一条消息 [图片] 好家伙,连fileId都给我了,不过有这个url其实已经可以下载了(有效期就是代码中的maxAge),具体的操作就由自己喜欢的方式来咯!在这里就不占用大家时间了 希望这篇文章能起到一点小小的作用
02-10 - 微信小程序 -- 基于 movable-view 实现拖拽排序
微信小程序 – 基于 movable-view 实现拖拽排序 项目基于[代码]colorui[代码]样式组件 ColorUI组件库 (color-ui.com) 1.实现效果 [图片] 2. 设计思路 movable-view 绑定块移动事件的 块[代码]ID[代码] ,块移动的坐标 移动结束后触发[代码]moveEnd[代码]事件,根据[代码]Y[代码]坐标对对象数组进行排序 根据排序结果重置块位置 3.实现代码 代码已经进行了最简化处理 图中效果实现需引入[代码]colorui[代码]的[代码]main.wxss[代码]样式部分。 wxml [代码]<movable-area class="padding text-center bg-grey" style="width:100%;height:500px;" > <movable-view class="radius shadow bg-white" style="width:80%;height:80px;z-index:{{index==moveId?2:1}}" wx:for="{{tabList}}" wx:key="index" x="{{item.x}}" y="{{item.y}}" direction="all" bindchange="moveStatus" bindtouchend='moveEnd' data-moveid="{{index}}"> {{item.name}}</movable-view> </movable-area> [代码] js [代码]var compare = function (obj1, obj2) { var val1 = obj1.y; var val2 = obj2.y; if (val1 < val2) { return -1; } else if (val1 >= val2) { return 1; } else { return 0; } } Page({ /** * 页面的初始数据 */ data: { branchid:'', appdocid:'', tabList:[ { name:'十步杀一人' }, { name:'千里不留行' }, { name:'事了拂衣去' }, { name:'深藏身与名' } ], //移动的是哪个元素块 moveId:null, //最终停止的位置 endX:0, endY:0 }, initMove(){ let tabList = this.data.tabList; var tarr = [] tabList.forEach(function(ele,index){ let obj = ele obj.id = index obj.x = 30 obj.y = 100*index +20 tarr.push(obj) }) console.log(tarr) this.setData({ tabList:tarr }) }, moveEnd(e){ console.log(e) var that = this; that.setData({ ["tabList["+that.data.moveId+"].x"]:that.data.endX, ["tabList["+that.data.moveId+"].y"]:that.data.endY },()=>{ let tabList = this.data.tabList; tabList = tabList.sort(compare); that.setData({ tabList },()=>{ setTimeout(function(){ that.initMove(); },500) }) }) //计算位置 }, moveStatus(e){ // console.log(e) //移动的块ID var moveid = e.currentTarget.dataset.moveid; //最终坐标 let x = e.detail.x let y = e.detail.y this.setData({ moveId:moveid, endX:x, endY:y }) }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { this.initMove(); } }) [代码] 4.参考文档 movable-view | 微信开放文档 (qq.com)
2021-06-17 - 微信小程序开发技巧总结(二) -- 文件的选取、移动、上传和下载
微信小程序开发技巧总结(二) – 文件的选取、移动、上传和下载 1.不同类型文件的选取 1.1 常用的图片 视频 对于大部分开发者来说,需要上传的文件形式主要为图片,微信为此提供了接口。 [代码]wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success (res) { // tempFilePath可以作为img标签的src属性显示图片 const tempFilePaths = res.tempFilePaths } }) [代码] 其次为视频文件的选取,微信也为此提供了接口。 [代码]wx.chooseVideo({ sourceType: ['album','camera'], maxDuration: 60, //视频长度 单位 s camera: 'back', //选取前置 摄像 还是 后置 摄像 success(res) { console.log(res.tempFilePath) } }) [代码] 也为其提供了视频和图片的二合一接口,这个接口不建议调用,图片和视频的上传建议区分开。 [代码]wx.chooseMedia({ count: 9, mediaType: ['image','video'], sourceType: ['album', 'camera'], maxDuration: 30, camera: 'back', success(res) { console.log(res.tempFilePath)con console.log(res.size) } }) [代码] 这几个接口调用成功的回调函数中,都返回的是文件在文件在本机中的路径。 [代码]res.tempFilePath [代码] 这是一个 数组,存放着选择的所有文件的路径,用于上传。 1.2 其余形式各种文档 那么如果是想要在小程序中上传 doc、pdf、ppt 等类型的文件怎么处理?首先要注意的是微信小程序并没有给用户提供文件管理器接口。 开发者:我想要个文件管理器接口! 官方:不,你不想 聪明的开发者他没有办法,只能另辟蹊径。微信提供了一个选择客户端会话文件的方法。 [代码]wx.chooseMessageFile({ success(res){ console.log(res.tempFilePath) } }) [代码] 与上面两个接口相同,返回的也是文件在本地的存储路径,但是不同的是,这个接口可以选取全部的文件类型。 开发者如果想要上传非图片和视频内容的话,需要两步走。 打开微信文件传输助手,将想要上传的文件发送到文件传输助手 在小程序中调用这个接口,选择文件传输助手,从会话中选择想要上传的文件。 [图片] [图片] 2.文件的上传 2.1 uploadFile方法 所有的文件都是以字节流的形式进行上传,所以上传形式并没有什么本质区别,都是调用相应的接口进行上传。 小程序端写法如下: [代码]wx.uploadFile({ url: '你的服务器函数地址', //仅为示例,非真实的接口地址 filePath: '需要上传的文件路径', //res.tempFilepaths name: 'file', // 文件对应的key ,默认 为 file formData: { 'user': 'test' }, //上传额外携带的参数 success (res){ const data = res.data //do something } }) [代码] 2.2 服务器端如何处理上传的文件 服务端如何接受文件的上传?仅展示Java方式(SpringBoot 框架) [代码]@Controller @ResponseBody public class FileController { //文件上传控制类,是核心配置类,Win <->Linux @RequestMapping(value = "/upload/images") public String uploadimages(HttpServletRequest request,@RequestParam("file") MultipartFile file, @RequestParam("user") String user) throws IOException { //更换服务器,这个值也需要修改 //图片上传写法 //type 是上传图片的类型 if(!file.isEmpty()){ //文件不为空 //String path = "E:"+File.separator+"images"+File.separator+type; // this is windows method String path = "/share"+File.separator+"images"+File.separator+type; // this is Linux method String filename = file.getOriginalFilename(); File filepath = new File(path,filename);//新建文件存储路径 System.out.println(filepath); if(!filepath.getParentFile().mkdirs()){ filepath.getParentFile().mkdirs(); } file.transferTo(new File(path+File.separator+filename)); //想要返回可直接访问的链接还要配置 映射,具体请看下面链接 return "你的IP地址"+"/images/"+type+"/"+filename; }else { return "error"; } } } [代码] 配置访问映射 文件访问映射 2.3 云开发文件上传 微信小程序支持云开发,其文件上传接口有一些差异,但是不需要自己再构建后台。 [代码]wx.cloud.uploadFile({ cloudPath: 'file/' + '你的文件名字(带格式后缀)', // 在云端存储的路径 filePath: '', // 就是选择文件返回的路径 }).then(res => { // get resource ID console.log(res.fileID) }).catch(error => { // handle error }) [代码] 我们可以根据返回的fileID 置换 真实的文件访问地址。 其置换方式参见官方文档: [代码]wx.cloud.getTempFileURL({ fileList: ['cloud://xxx', 'cloud://yyy'], success: res => { // get temp file URL console.log(res.fileList) }, fail: err => { // handle error } }) [代码] TIPS:关于云开发文件上传的建议 如果没有保密需求,为了方便后续开发,存储到数据库中的最好是可以直接访问的文件链接。 置换真实文件地址,不要每次上传一次文件就置换一次,先把返回的fileID 存放在数组中,到该事务所有上传完成后,再使用fileID 数组置换真实文件访问链接数组。 要考虑文件名重复的可能,建议使用时间戳在 wx.cloud.uploadFile 中的 cloudPath 中对存储到云环境中的文件命名进行格式化。 总的来说就是先上传文件,再向数据库中写入记录。 2.4 多文件同时上传的处理方式 uploadFile 每次只能上传一个文件 如何处理这个问题? A.不考虑文件的上传次序问题,可以采用遍历上传的方式,采用精确的时间戳和遍历index对文件名进行格式化。通过定时触发检测函数判断是否全部上传完成。这种方式考虑的是并发能力。 [代码]upSeveralfiles() { wx.showLoading({ title: '上传中~', mask: true }) var that = this; var timecode = sev.vcode(new Date()); // 这是时间戳编码函数 var files = this.data.fileList; // 这是 选择文件中返回的 res.tempFilePath 数组 var len = files.length; var i = 0; for (i = 0; i < len; i++) { var str = files[i].name; wx.cloud.uploadFile({ cloudPath: 'file/' + '(' + sev.getformatTime(new Date()) + ')' + str, filePath: files[i].path, success(res) { console.log(res) that.setData({ cloudlist: that.data.cloudlist.concat([res.fileID]), }) // cloudlist 是存放 文件链接置换id 的数组 , 非云开发存储的就是真实可访问的链接数组 // 如果使用的不是云开发 那么 可以返回真实的 访问地址 }, fail(res) { console.log(res) } }) } // 使用定时器检测文件是否全部上传完成 , 并 判断是否进行下一步 操作 var timer = setInterval(function () { if (that.data.cloudlist.length == len) { // 只有全部上传成功了 长度才会相等 clearInterval(timer); // 继续执行下一步 ,根据 cloudlist 置换真实地址 并存放到数据库 // 如果使用的非云开发,那么就继续执行 存储至数据库的操作 } }, 1000) } [代码] 补充文件编码函数 sev.js 中的根据时间编码部分 , 可以根据实际流量自定义。 [代码]function getformatTime(date) { var year = date.getFullYear() var month = date.getMonth() + 1 var day = date.getDate() var hour = date.getHours() var minute = date.getMinutes() var second = date.getSeconds() return [year, month, day].map(formatNumber).join('-'); }; [代码] B.考虑文件的上传次序问题,采用回调方式进行文件上传(更推荐使用这种方式) [代码]data: { fileList: [], realList: [],//云端地址链接列表 fileid: 0, }, upSeveralfiles() { var that = this; var files = this.data.fileList; // 这是 选择文件中返回的 res.tempFilePath 数组 var len = files.length; var uid = this.data.fileid; wx.uploadFile({ url: '你的服务器文件接收函数请求地址', name: 'file', filePath: files[uid], header: { "Content-Type": "multipart/form-data" }, success(res) { that.setData({ fileid: uid + 1, realList: that.data.realList.concat([res.data]) }, () => { if (that.data.fileid == len) { // 上传完成 ,执行下一步操作 } else { //上传完一个文件 递归执行 下次上传 that.upSeveralfiles(); } }) }, fail(res) { console.log(res.data) } }) }, [代码] 以上是提供的两种思路 , 无论是不是云开发 , 两种思路都是共通的,适用于多文件上传. 文件的下载 这个地方唯一值得注意的是云开发的一种下载方式 可以通过fileID进行download , 当然只能下载存储在自己云环境中的文件. [代码]wx.cloud.downloadFile({ fileID: '', success: res => { console.log(res.tempFilePath) }, fail: err => { } }) [代码] [代码]wx.downloadFile({ url: '', //仅为示例,并非真实的资源 success (res) { //res.tempFilePath }) [代码] 下载进度监控 , 用于下载进度条绘制等功能实现 [代码]const downloadTask = wx.cloud.downloadFile({ *** }) // wx.downloadFile 同理 downloadTask.onProgressUpdate((res) => { //res.progress 为下载进度 }) [代码] 下载下来的文件支持直接打开,就像是在微信聊天中打开一样,需要下载插件。 其使用方式为: [代码]wx.openDocument({ filePath: res.tempFilePath //为文件路径 非数组 , 可使用回调函数 success 等 }) [代码] 文件的移动 这个一般情况下是用不到的, 也不建议使用移动文件的方法作为功能实现手段 ,必然有更好的替代方式,比如修改数据库路径 和 真实文件路径的映射, 效率更高一些. 这里仅讲云开发移动文件方式。 [代码]const cloud = require('wx-server-sdk') const fs = require('fs') const path = require('path') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main = async (event, context) => { const fileStream = fs.createReadStream(path.join(__dirname, 'demo.jpg')) //这个可以读取 云存储 中的文件 , 所谓删除 就是复制 然后 删除原位置文件 return await cloud.uploadFile({ cloudPath: 'demo.jpg', fileContent: fileStream, }) } [代码] 具体开发文档 建议在数据库中修改映射路径最好。 需要小程序开发的请联系我QQ : 1025584691
2020-02-17 - 免费ICP备案攻略。不花1分钱拥有一台云服务器并顺利ICP备案。
写在前面: 大家不要将ICP证和ICP备案搞混了。 ICP证指的是【电信增值业务经营许可证】,这个资质需要企业主体至少100万注金,去工信部办理,比较难办理;社交-交友需要ICP证。 而ICP备案,【非经营性互联网信息服务备案核准】仅仅是指企业主体的域名备案,可以简单的按以下步骤免费办理成功,其他社交类目如社区、论坛、笔记等,只需要ICP备案即可。 1、在腾讯云注册一个账号并认证企业主体(不吹不黑,开发小程序当然首选腾讯云,好用)。http://www.qcloud.com/ 如果你是个人主体,就不要往下看了,没必要折腾了。 2、找到腾讯云免费活动页:https://cloud.tencent.com/act/free?from=10107 3、选择一款云服务器,180天免费试用。 云服务器申请成功后,它的使命就完成了,没用了,让它自生自灭吧。 在整个备案过程中,也不需要部署网站(域名都没有备案,哪来的网站?)。 [图片] 云服务器180天到期后,可以自己决定是否续费,每个月也才99元,促销期甚至更低,完全可以接受吧。 备案成功后,该服务器就没什么作用了,让它180天后自然欠费销毁得了。 服务器销毁后会有什么影响?答:没有任何影响。 但是。。。。。 你备案的域名最后还得指向一个网站,因为腾讯云会应工信部的要求定期检查网站是否合规,所以你还是要建一个简单的网站,(备案期间,可以暂时不管网站的事,等将来需要的时候再管理)。 至于有多简单,答,多简单都行。此时你可以在七牛、腾讯云、阿里云租点免费的对象存储空间,做个简单的网站。 4、在进行ICP备案之前,你需要在腾讯云注册你的域名地址,如果你已有域名,但不在腾讯云,建议先将要域名过户到腾讯云的账号上。 5、进入控制台,开始ICP备案,这个流程就不介绍了,因为完全一看就懂。而且现在使用备案小程序后,不需要幕布或现场拍照了,极其方便,大家跟着流程走就一点问题没有,有人脸识别和在线拍一段小视频。另外,大家可以随便作,随便填,填错或者填得不合适也不用怕,会有专门的备案客服打电话告诉你哪哪要改,还会告诉你应该怎么填才更容易通过工信部的审核,客服的态度好得发指。 仅说一点其中的几个小坑: a、人脸识别的时候,白色背景、白色背景、白色背景,笔者在人脸识别的时候,满世界找白墙,结果还被打回来重拍了3次。 b、网站用途一律写:公司官网,好通过工信部审核。 6、腾讯云提交资料到工信部审核。这是一个漫长的让人无语的等待,20-30天。笔者最近两次都是20天才过审;不要幻想会有可能提前完成审核,这是政府部门在审核,提前完成说明某政府人员的工作安排有问题,会犯错误的。 7、备案成功后,会有短信通知你,但是,你需要去工信部网站查询结果,并将结果切屏拷贝下来,因为小程序类目审核需要上传这张图片。http://beian.miit.gov.cn/publish/query/indexFirst.action [图片] 把上面这张图片保存好,小程序类目审核的时候需要上传。收到通知后,如果在这里查不到结果,也别急,据说需要24小时。 8、接下来是小程序上线审核。 因为ICP备案的小程序内容肯定涉及到社交,最后小程序上线时还要提交到工信部审核,还需要7天左右的时间,加上前面ICP备案的时间,加起来怎么也得30-40天。大家估计时间,别影响小程序上线。这7天也是政府部门在审核,不要幻想会提前。 9、计算一下时间: 腾讯云注册账号和认证:1-3天; 域名备案:腾讯云环节:1-3天; 域名备案:工信部环节:20-30天; 小程序添加服务类目:社交类目审核:1-3天; 小程序上线审核:腾讯环节:1-2天; 小程序上线审核:工信部环节:7+天; 总天数:30-40天; 10、节省时间的一些建议: 在开发小程序之前,就开始备案工作,小程序可以同时开发,相互不影响; 在开发完成之前一、两星期之内,先发布一版小程序,别管功能是不是完整,能通过审核就行,这样会有7天的等待类目审核的时间,这个时间里,小程序可以照常开发,不影响进度; 只要是社交类,基本需要有文字和图片安全检查功能,别忘了加上,别到时审核通过不了。 11、结束。 [图片]
2021-01-19 - 只有小程序,没有域名和网站,如何办理《非经营性互联网信息服务备案核准》?
你好。前段时间,我小程序上传了养生类文章、视频课件,审核时不通过,要求办理《非经营性互联网信息服务备案核准》,我问信管局,他们说:小程序不用办理备案,而且小程序没有域名和网站,也没办法办理备案。请问我这种情况,小程序要想上传了养生类文章、视频课件,应该如何办理备案才能通过审核。谢谢
2019-12-18 - 小程序导出数据到excel表,借助云开发后台实现excel数据的保存
我们在做小程序开发的过程中,可能会有这样的需求,就是把我们云数据库里的数据批量导出到excel表里。如果直接在小程序里写是实现不了的,所以我们要借助小程序的云开发功能了。这里需要用到云函数,云存储和云数据库。可以说通过这一个例子,把我们微信小程序云开发相关的知识都用到了。 老规矩,先看效果图 [图片] 上图就是我们保存用户数据到excel生成的excel文件。 实现思路 1,创建云函数 2,在云函数里读取云数据库里的数据 3,安装node-xlsx类库(node类库) 4,把云数据库里读取到的数据存到excel里 5,把excel存到云存储里并返回对应的云文件地址 6,通过云文件地址下载excel文件 一,创建excel云函数 关于云函数的创建,我这里不多说了。如果你连云函数的创建都不知道,建议你去小程序云开发官方文档去看看。或者看下我录制的云开发入门的视频:https://edu.csdn.net/course/detail/9604 创建云函数时有两点需要注意的,给大家说下 1,一定要把app.js里的环境id换成你自己的 [图片] 2,你的云函数目录要选择你对应的云开发环境(通常这里默认选中的) 不过你这里的云开发环境要和你app.js里的保持一致 [图片] 二,读取云数据库里的数据 我们第一步创建好云函数以后,可以先在云函数里读取我们的云数据库里的数据。 1,先看下我们云数据库里的数据 [图片] 2,编写云函数,读取云数据库里的数据(一定要记得部署云函数) [图片] 3,成功读取到数据 [图片] 把读取user数据表的完整代码给大家贴出来。 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init({ env: "test-vsbkm" }) // 云函数入口函数 exports.main = async(event, context) => { return await cloud.database().collection('users').get(); } [代码] 三,安装生成excel文件的类库 node-xlsx 通过上面第二步可以看到我们已经成功的拿到需要保存到excel的源数据,我们接下来要做的就是把数据保存到excel 1,安装node-xlsx类库 [图片] 这一步需要我们事先安装node,因为我们要用到npm命令,通过命令行 [代码]npm install node-xlsx [代码] [图片] 可以看出我们安装完成以后,多了一个package-lock.json的文件 [图片] 四,编写把数据保存到excel的代码, 下图是我们的核心代码 [图片] 这里的数据是我们查询的users表的数据,然后通过下面代码遍历数组,然后存入excel。这里需要注意我们的id,name,weixin要和users表里的对应。 [代码] for (let key in userdata) { let arr = []; arr.push(userdata[key].id); arr.push(userdata[key].name); arr.push(userdata[key].weixin); alldata.push(arr) } [代码] 还有下面这段代码,是把excel保存到云存储用的 [代码] //4,把excel文件保存到云存储里 return await cloud.uploadFile({ cloudPath: dataCVS, fileContent: buffer, //excel二进制文件 }) [代码] 下面把完整的excel里的index.js代码贴给大家,记得把云开发环境id换成你自己的。 [代码]const cloud = require('wx-server-sdk') //这里最好也初始化一下你的云开发环境 cloud.init({ env: "test-vsbkm" }) //操作excel用的类库 const xlsx = require('node-xlsx'); // 云函数入口函数 exports.main = async(event, context) => { try { let {userdata} = event //1,定义excel表格名 let dataCVS = 'test.xlsx' //2,定义存储数据的 let alldata = []; let row = ['id', '姓名', '微信号']; //表属性 alldata.push(row); for (let key in userdata) { let arr = []; arr.push(userdata[key].id); arr.push(userdata[key].name); arr.push(userdata[key].weixin); alldata.push(arr) } //3,把数据保存到excel里 var buffer = await xlsx.build([{ name: "mySheetName", data: alldata }]); //4,把excel文件保存到云存储里 return await cloud.uploadFile({ cloudPath: dataCVS, fileContent: buffer, //excel二进制文件 }) } catch (e) { console.error(e) return e } } [代码] 五,把excel存到云存储里并返回对应的云文件地址 我们上面已经成功的把数据存到excel里,并把excel文件存到云存储里。可以看下效果。 [图片] 我们这个时候,就可以通过上图的下载地址下载excel文件了。 [图片] 我们打开下载的excel [图片] 其实到这里就差不多实现了基本的把数据保存到excel里的功能了,但是我们要下载excel,总不能每次都去云开发后台吧。所以我们接下来要动态的获取这个下载地址。 六,获取云文件地址下载excel文件 [图片] 通过上图我们可以看出,我们获取下载链接需要用到一个fileID,而这个fileID在我们保存excel到云存储时,有返回,如下图。我们把fileID传给我们获取下载链接的方法即可。 [图片] 1,我们获取到了下载链接,接下来就要把下载链接显示到页面 [图片] 2,代码显示到页面以后,我们就要复制这个链接,方便用户粘贴到浏览器或者微信去下载 [图片] 下面把我这个页面的完整代码贴给大家 [代码]Page({ onLoad: function(options) { let that = this; //读取users表数据 wx.cloud.callFunction({ name: "getUsers", success(res) { console.log("读取成功", res.result.data) that.savaExcel(res.result.data) }, fail(res) { console.log("读取失败", res) } }) }, //把数据保存到excel里,并把excel保存到云存储 savaExcel(userdata) { let that = this wx.cloud.callFunction({ name: "excel", data: { userdata: userdata }, success(res) { console.log("保存成功", res) that.getFileUrl(res.result.fileID) }, fail(res) { console.log("保存失败", res) } }) }, //获取云存储文件下载地址,这个地址有效期一天 getFileUrl(fileID) { let that = this; wx.cloud.getTempFileURL({ fileList: [fileID], success: res => { // get temp file URL console.log("文件下载链接", res.fileList[0].tempFileURL) that.setData({ fileUrl: res.fileList[0].tempFileURL }) }, fail: err => { // handle error } }) }, //复制excel文件下载链接 copyFileUrl() { let that=this wx.setClipboardData({ data: that.data.fileUrl, success(res) { wx.getClipboardData({ success(res) { console.log("复制成功",res.data) // data } }) } }) } }) [代码] 给大家说下上面代码的步骤。 1,下通过getUsers云函数去云数据库获取数据 2,把获取到的数据通过excel云函数把数据保存到excel,然后把excel保存的云存储。 3,获取云存储里的文件下载链接 4,复制下载链接,到浏览器里下载excel文件。 到这里我们就完整的实现了把数据保存到excel的功能了。 文章有点长,知识点有点多,但是大家把这个搞会以后,就可以完整的学习小程序云开发的:云函数,云数据库,云存储了。可以说这是一个综合的案例。 有什么不懂的地方,或者有疑问的地方,请在文章底部留言,我看到都会及时解答的。后面我还会出一系列关于云开发的文章,敬请关注。
2019-09-07 - 微信小程序之swiper轮播图片高度自适应
微信小程序中使用swiper组件可以实现图片轮播效果,但是默认swiper高度是固定的150px,如果项目中图片大于固定高度就会被隐藏,所以本篇文章要实现轮播图片的高度自适应。 效果图: [图片] wxml代码: [代码]<[代码][代码]swiper[代码] [代码]class[代码][代码]=[代码][代码]"t-swiper"[代码] [代码]indicator-dots[代码][代码]=[代码][代码]"{{indicatordots}}"[代码] [代码]indicator-active-color[代码][代码]=[代码][代码]"{{color}}"[代码] [代码]autoplay[代码][代码]=[代码][代码]"{{autoplay}}"[代码] [代码]interval[代码][代码]=[代码][代码]"{{interval}}"[代码] [代码]duration[代码][代码]=[代码][代码]"{{duration}}"[代码] [代码]style[代码][代码]=[代码][代码]"height:{{height}}"[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]block[代码] [代码]wx:for[代码][代码]=[代码][代码]"{{img}}"[代码] [代码]wx:key[代码][代码]=[代码][代码]""[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]swiper-item[代码][代码]>[代码][代码] [代码][代码]<[代码][代码]image[代码] [代码]src[代码][代码]=[代码][代码]"{{item}}"[代码] [代码]mode[代码][代码]=[代码][代码]"widthFix"[代码] [代码]bindload[代码][代码]=[代码][代码]'goheight'[代码] [代码]/>[代码][代码] [代码][代码]</[代码][代码]swiper-item[代码][代码]>[代码][代码] [代码][代码]</[代码][代码]block[代码][代码]>[代码][代码]</[代码][代码]swiper[代码][代码]>[代码]wxss代码: [代码].t-swiper image {[代码][代码] [代码][代码]width[代码][代码]: [代码][代码]100%[代码][代码];[代码][代码]}[代码]js代码: [代码]Page({[代码][代码] [代码][代码]data: {[代码][代码] [代码][代码]img: [[代码][代码] [代码][代码]'img/1.jpg'[代码][代码],[代码][代码] [代码][代码]'img/2.jpg'[代码][代码],[代码][代码] [代码][代码]'img/3.jpg'[代码][代码] [代码][代码]],[代码][代码] [代码][代码]indicatordots: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]//是否显示面板指示点[代码][代码] [代码][代码]autoplay: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]//是否自动切换[代码][代码] [代码][代码]interval: 5000,[代码][代码] [代码][代码]//自动切换时间间隔[代码][代码] [代码][代码]duration: 500,[代码][代码] [代码][代码]//滑动动画时长[代码][代码] [代码][代码]color: [代码][代码]'#ffffff'[代码][代码],[代码][代码] [代码][代码]//当前选中的指示点颜色[代码][代码] [代码][代码]height: [代码][代码]''[代码][代码] [代码][代码]//swiper高度[代码][代码] [代码][代码]},[代码][代码] [代码][代码]goheight: [代码][代码]function[代码] [代码](e) {[代码][代码] [代码][代码]var[代码] [代码]width = wx.getSystemInfoSync().windowWidth[代码][代码] [代码][代码]//获取可使用窗口宽度[代码][代码] [代码][代码]var[代码] [代码]imgheight = e.detail.height[代码][代码] [代码][代码]//获取图片实际高度[代码][代码] [代码][代码]var[代码] [代码]imgwidth = e.detail.width[代码][代码] [代码][代码]//获取图片实际宽度[代码][代码] [代码][代码]var[代码] [代码]height = width * imgheight / imgwidth + [代码][代码]"px"[代码][代码] [代码][代码]//计算等比swiper高度[代码][代码] [代码][代码]this[代码][代码].setData({[代码][代码] [代码][代码]height: height[代码][代码] [代码][代码]})[代码][代码] [代码][代码]}[代码][代码]})[代码]
2019-07-14 - 小程序将小程序码与图片结合生成海报分享朋友圈
样例参考(瑞幸咖啡小程序) [图片][图片][图片] 需求分析 服务器端会返回不确认的图片资源到前端 前端将返回的每张图片都要贴上小程序码 将贴上小程序码的图片使用 swiper 组件轮播 用户点击保存时,将图片保存至相册。 至于点击保存如何保存至相册(wx.saveImageToPhotosAlbuml 了解一下,注意一下授权问题即可) 碰到的问题 canvas为原生组件, 而原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。 [代码]采用方法: 通过定位,将其移除到不在可视范围,如iphone6 .canvas { position: relative; left: -375px; } [代码] 绘制多张图片时, 一个canvas 标签只能对应画一张图片 (目前我测试的是这样,如有其它方法,欢迎评论交流) [代码]采用方法: 1. html端循环要生成的图片张数,对应循环出多个 canvas 组件,分别设置不同的 canvasId 区分 2. 封装一个绘制海报的函数,返回一个Promise对象,用于后面绘制完所有的图片,统一赋值渲染,避免多次触发数据更新 3. js端 循环执行一次 绘制海豹函数,存于一个数组列表 4. 使用 Promise.all 函数,统一绘制完毕将生产图片路径赋值 html: <canvas v-for="u in m.urlList" :key="u" :canvas-id="'poster'+index" class="canvas" style="width:100%; height:100%;"> </canvas> <swiper :indicator-dots="true" :circular="true" :autoplay="true" indicator-color="rgba(255,255,255,.2)" indicator-active-color="#fff" class="swiper"> <swiper-item v-for="f in m.filePaths" :key="index"> <image :src="f"> </swiper-item> </swiper> js: // 数据 (mpvue 开发) function data(){ return { m : { urlLIst: [ // 图片资源 '/static/poster0.jpg', '/static/poster1.jpg' ], filePaths: [], // 生成图片(贴上小程序码) } } } try { let res = wx.getSystemInfoSync(); // 同步获取系统信息 let w = res.windowWidth; // 手机可用区域宽度 let h = res.windowHeight; // 手机可用区域高度 let codeUrl = '/static/code.jpg'; // 小程序码 let drawList = []; // 用于保存绘制海报图Promise对象 m.urlList.forEach( (u, i)=> { // 传入canvas组件ID,图片路径(测试使用的是本地路径) drawList.push(drawPoster('poster'+i, u, codeUrl)); }) // 统一更新数据 Promise.all(drawList).then((valuse)=>{ m.filePaths = valuse; }); // 封装绘制图片函数 function drawPoster(canvasId, bgUrl, codeUrl){ return new Promise( (resolve, reject) => { // 创建画布实例 let ctx = wx.createCanvasContext(canvasId); // 绘制背景图: 图片路径,x坐标,y坐标,宽,高 ctx.drawImage(bgUrl, 0, 0, w, h); // 绘制小程序码 ctx.drawImage(codeUrl, w-120, h-120, 100, 100); // 绘制 ctx.draw(false, ()=>{ // 该通过函数将canvas绘制导出为图片 wx.canvasToTempFilePath({ x: 0, y: 0, width: w, height: h, canvasId: canvasId, success(res){ resolve(res.tempFilePath); } }); }); } }catch(e){ // 自己封装了一成 wx.$toast(e); } [代码] 最终demo效果图 [图片][图片] 在社区中暂未看到多张海报实现的方案,如果有更好的实现方案,欢迎交流
2019-07-30 - 《非经营性互联网信息服务备案核准》是不是就是 ICP 备案?
[图片] 非个人主体中的开放的服务类目 > 社区/论坛所要求的法务建议的资质文件 《非经营性互联网信息服务备案核准》是不是就是 ICP 备案? [图片] 像这样普通备案就可以呗?
2018-11-06 - # 使用小程序云开发API更新数组中的单个数组元素
使用小程序云开发API更新数组中的单个数组元素 看了看mongoDB的更新数据方式,找到了解决办法,解决方法如下,亲测可用: 第一种方法:使用位置操作符$ [代码]代码,条件更新写在云函数中 [代码] [图片] [代码]test_api集合原始数据如下 [代码] [图片] [代码]在云函数中执行1中的代码,数组users中id为1001的用户添加了一个新的属性test [代码] [图片] [代码]原理分析 [代码] where条件是查找数组中id属性为1001的用户 update中的使用’users.$.test’: ‘test’ 注意里面的$符号,在mongoDB中,这个符号叫做位置操作符,代表数组的下标,如下引自《mongoDB实战》 [图片] 第二种方法:直接使用数组下标 云函数代码 [图片] test_api集合原始数据如下 [图片] 代码执行后 [图片] 相对于第一种方法,这种方法更加简单,只不过users.1.test这种写法有点颠覆js和java中的属性书写规则,让人感觉怪怪的,在mongoDB中,也支持点数字这种写法。 一个可能的疑惑 可不可以写作’users[1].test’:‘test’,测试结果如下: [图片] [图片] 可以看到’user[1]'无法被识别为数组的第二个元素,而是作为了属性名新增了一个属性,结论:必须写成”点数字“不能写成“中括号” 结论: 经过测试,使用这两种种方法可以更新数组中的一个元素。 方法一适合在不知道数组元素下标的情况下根据查询条件更新元素; 方法二适合在知道数组元素下标的情况下更新元素; 当然也存在既知道元素下标也可以通过属性查到的情况,想用哪个就看心情了-.- 但是暂未找到查询返回数组中的一个元素的方法,再探索探索吧 ——。——
2019-03-06