前言
第一次写,有什么写的不清楚的地方大家指点。
我本是做前端的,干了6年左右,前一阵接了个私活,做微信小程序。技术上小程序用的原生+vant,后台管理系统用的Vue+Element,本来是前后端分离按照正常流程去做,结果后端拉胯走了(He Tui~),最后因为前期用户量并不多,决定用小程序云开发先挺过这段时间。
虽然小程序干了不老少,后端只会Python,但是我也是第一次用云开发写后端逻辑和操作数据库,不太熟悉,感觉会遇到很多坑,这就遇到了上传这个大坑。
需求介绍
功能需求:小程序端是一般的表单提交+上传最多三张图片,后台管理系统也有上传,要求通过云开发中部署静态网站,调用云函数完成所有数据交互。
碎碎念:接活之前已经用测试账号做过技术验证,也没什么问题,但是心里总有一种不安,感觉上传这块要遇大坑,想什么来什么。
前期技术验证
由于是第一次做,肯定前期会看很多相应的文章或者逛逛社区,总的来讲网上的文章目前可以分为三类:
- 在客户端使用wx.cloud.uploadFile上传图片
- 将图片转为base64传至后台,用Buffer.from转回来存入云储存
- 使用wx.cloud.CDN传输大文件,用Buffer.from转回来存入云储存
各方法的从开始到放弃
方法1,从想法到放弃
上边也说了,我们完全没有后端,而且也确实需要web调用云函数,并且我希望所有数据库操作和上传等均在云函数中进行。所以在web端只用SDK调callFunction方法去做数据交互,再加上权限等问题果断放弃方法1。有老大就要说了,那就用先用wx.cloud.uploadFile解决小程序后边再说嘛,反正早晚也得解决,不如直接解决了算了(我不,强迫症,爱咋咋地)。
方法2,从验证成功到莫名报错到放弃
对于没有干过的技术方案怎么能不认真验证可行性呢,坑了甲方咋办!(主要是怕扣我钱,我的小钱钱~),咳咳,言归正传。
总的思路是这样的:(前期云开发准备工作跳过,自己搜去)
(1)首先通过wx.chooseImage选择图片
chooseImages(){
let _this = this
wx.chooseImage({
count: 3,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success(res){
const tempFilePaths = res.tempFilePaths
_this.setData({
images: tempFilePaths
})
}
})
}
(2)使用 wx.getFileSystemManager().readFile()将图片转成base64字符串,然后callFunction传过去,如果多张上传就循环。
getImageBase64(){
wx.getFileSystemManager().readFile({
filePath: images,
encoding: 'base64',
success(response){
wx.cloud.callFunction({
name: 'quickstartFunctions',
config: {
env: '******'
},
data: {
type: 'uploadPicture',
path: 'images/test.png',
file: response.data
},
success(res) {
console.log(res)
}
})
}
})
}
(3)在云函数中接到base64然后转回来上传
const cloud = require('wx-server-sdk');
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
});
exports.main = async (event, context) => {
try {
return await cloud.uploadFile({
cloudPath: event.path,
fileContent: Buffer.from(event.file, 'base64')
})
} catch (e) {
return e;
}
}
神奇的来了,技术验证顺风顺水,在实际开发中,CV大法过来改了改,结果报!错!了!连云函数都没进去。报了-404006
面向百度变成的我,百了各种度,都没找到原因,但是只要不传base64就能进云函数,明明验证的代码还在那摆着,可他依旧报错坚挺,而且至今都不知道为啥(有知道的告诉告诉孩子吧,孩子不容易啊)
既然找不到原因那就果断放弃吧,毕竟世上无难事,只要肯放弃,我转向了方法3
方法3,从瞎蒙到抓狂到成功
官方对wx.cloud.CDN的定义是这样的↓↓↓↓↓↓↓↓↓↓↓↓
示例代码更是简单的不能再简单
OK,我们能从这上边得出什么呢?当然啥都看不出来,TX技术文档某些部分的模糊程度大家都懂的,反正我看的云里雾里的。简单来说呢,wx.cloud.CDN需要放在wx.cloud.callFunction中用,就是用来传大文件的,它会在云函数中对应字段返回一个地址,通过地址拿到你传的数据,但是怎么拿,他!没!说!(Tui~)
下面看我的代码,由于昨天弄了一天,虽然也是按照CDN方式弄的,但是图片无法查看。今天莫名其妙的调试成功了,还没来得及整理代码(凑合看吧)
获取图片的步骤跟上边一样,可是为什么不用现成的代码块可以复制粘贴呢,因为我不想让你们粘,主要看imageUpload中的代码,最后我会解释为啥我上边刚说的wx.cloud.callFunction用,这就出幺蛾子了。
解释一下,一共三个参数,file,path,suffix。file是CDN地址,把chooseImage获取的图片临时文件地址放入CDN中,返回个地址当做参数传到云函数中,path是云储存的地址,我只传了一个文件夹地址,剩下的交给云函数生成,suffix是文件后缀,我也不知道传的png还是jpg,而且保证上传的复用性,所以获取了个后缀一起传过去了,就这么简单。
重点来了,怎么获取CDN地址中的数据嘞~有请axios,熟悉不,就是Vue中的axios,请求一下子不就好了。抄家伙!在云函数中安装依赖npm install axios --save,先安上,要不准报错undefined。
也有人用got,我也用过了,不知道是不是版本原因不能用require引入,云函数又不能import,放弃。经过我的不懈努力!OK看代码
const cloud = require('wx-server-sdk')
const PARAMS = require('../params.json') // 配置JSON
const SendData = require('../sendData/index') // 发送数据回来的函数,加了code和是否成功
const axios = require('axios') // 引入axios
cloud.init({
env: PARAMS.env
})
exports.main = async (event, context) => {
try {
// cloud.uploadFile可以接arraybuffer,所以获取类型设置为arraybuffer
let imgBuffer = await axios({
method:'get',
url:event.data.file,
responseType: 'arraybuffer',
headers: { "Content-Type": "*" },
})
// 上传
const cloudId = await cloud.uploadFile({
cloudPath: event.data.path+'U'+Date.now()+event.data.suffix,
fileContent: Buffer.from(imgBuffer.data)
})
// 成功后返回云储存的fileID
return SendData(cloudId.fileID)
} catch (e) {
return SendData('图片上传失败', 200, false)
}
}
看看储存里,这就上传成功了。
函数SendData中return的就是个JSON不用纠结我怎么写的,我只是懒。
其他
刚才上边说CDN只能放在callFunction中使用,但是我放在了imageUpload中,心机之蛙一直摸你肚子!这就是我自己封装的,咳咳。我可不想每次都得写wx.callFunction巴拉巴拉,呸,代码太多了!
其实本质就是callFunction,简单介绍一下我的做法,我将callFunction封装成了一个函数Call,把请求的云函数名和数据等进行操作整理,还有自定义loading文字,然后把Call暴露出来,建立api文件夹,引入Call,把所有的交互函数当做api一样进行统一管理。如果大家有兴趣我抽空可以把代码扔出来大家喷喷。
如果大家觉得有帮助,做后台管理系统接口的时候再发一期怎么在vue项目中使用云函数,并且部署,我去整理代码,继续搬砖了,散会