前言

        第一次写,有什么写的不清楚的地方大家指点。

        我本是做前端的,干了6年左右,前一阵接了个私活,做微信小程序。技术上小程序用的原生+vant,后台管理系统用的Vue+Element,本来是前后端分离按照正常流程去做,结果后端拉胯走了(He Tui~),最后因为前期用户量并不多,决定用小程序云开发先挺过这段时间。

        虽然小程序干了不老少,后端只会Python,但是我也是第一次用云开发写后端逻辑和操作数据库,不太熟悉,感觉会遇到很多坑,这就遇到了上传这个大坑。

需求介绍

        功能需求:小程序端是一般的表单提交+上传最多三张图片,后台管理系统也有上传,要求通过云开发中部署静态网站,调用云函数完成所有数据交互。

        碎碎念:接活之前已经用测试账号做过技术验证,也没什么问题,但是心里总有一种不安,感觉上传这块要遇大坑,想什么来什么。

前期技术验证

        由于是第一次做,肯定前期会看很多相应的文章或者逛逛社区,总的来讲网上的文章目前可以分为三类:

  1. 在客户端使用wx.cloud.uploadFile上传图片
  2. 将图片转为base64传至后台,用Buffer.from转回来存入云储存
  3. 使用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的定义是这样的↓↓↓↓↓↓↓↓↓↓↓↓

微信小程序在云服务上传和读取数据_ios_02

        示例代码更是简单的不能再简单

微信小程序在云服务上传和读取数据_微信小程序_03

        OK,我们能从这上边得出什么呢?当然啥都看不出来,TX技术文档某些部分的模糊程度大家都懂的,反正我看的云里雾里的。简单来说呢,wx.cloud.CDN需要放在wx.cloud.callFunction中用,就是用来传大文件的,它会在云函数中对应字段返回一个地址,通过地址拿到你传的数据,但是怎么拿,他!没!说!(Tui~)

        下面看我的代码,由于昨天弄了一天,虽然也是按照CDN方式弄的,但是图片无法查看。今天莫名其妙的调试成功了,还没来得及整理代码(凑合看吧)

微信小程序在云服务上传和读取数据_微信小程序在云服务上传和读取数据_04

        获取图片的步骤跟上边一样,可是为什么不用现成的代码块可以复制粘贴呢,因为我不想让你们粘,主要看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项目中使用云函数,并且部署,我去整理代码,继续搬砖了,散会