无服务器开发人脸识别小程序

前言

前段时间我还在想,如果小程序能使用无服务器计算产品那该多好,果不其然,最近微信与腾讯云联合开发的原生 serverless 云服务产品——小程序 · 云开发,其具备简化运维、高效鉴权等优势,让你零门槛快速上线小程序。为此,我决定尝试下这种新的开发方式,看看是不是真的如官方所说。

那么,用什么项目去尝试呢?看了下自己以前写的文章【如何在小程序中实现人脸识别功能】,发现这文章关注量还挺多,况且腾讯云人脸识别API于2019年1月25日全量更新为了最新的3.0版本,API调用方式及也有较大变化。完全可以用最新版的API结合云开发去体验下这个过程。

现小程序 · 云开发这款产品还在免费阶段,同时腾讯云人脸识别服务每月为各个接口提供 1 万次 的免费调用,对于此次实践来说性价比很高。若企业开发者需要大量的调用,还可以抓住腾讯云一年一度的“新春采购节”大促进行囤量,在3月5日-4月5日期间,AI专场的产品低至3折钜惠。

这篇文章将分享我开发过程中的一些思路,如何考虑产品应用性,如何优化逻辑等问题。同时也会分享整个的开发过程,从怎么注册账户到怎么调用API,以及代码是如何一点一点拼接的。大家也可以将这篇文章看为一篇教程,我会从0~1分享整个项目的开发过程。当然,如果你是一名Developer,请直接使用我撰写好的代码,已经分享到了GitHub,欢迎大家参阅!

准备

在撰写代码之前,我们需要先准备一下小程序的开发环境,所需要的环境有GitNodeJSnpm微信开发者工具。同时,因为要调用腾讯云人脸识别API,我们还需要注册腾讯云的账号,同时还需要注册腾讯云的API密钥

安装Git

git是一个分布式版本控制软件,开发小程序的时候做好代码版本控制非常重要,如果代码写错,可以使用Git快速恢复。安装Git较为简单,我们只需要打开Git官网,点击右侧的Download 2.20.1 for Windows,当然,版本号和系统都可能不同,大家按照自己的系统及最新的版本号下载即可。

下载完成后双击打开安装包,然后一路点击下一步直至安装完成。安装完成后我们按键盘上的Win+R,然后输入CMD打开命令提示符窗口,然后在命令提示符窗口中输入git,如果你看到类似下面的截图,证明你的Git安装成功,可以进行下一步了。

安装NodeJS和npm

NodeJS是一个可以跨平台在服务端运行JavaScript的运行环境,我们小程序云开发所使用的服务端环境就是NodeJS,为了优化并测试代码,建议在本地安装NodeJS运行环境。npm是Node包管理器,通过npm包管理器,我们可以非常方便的安装云开发所需要的环境。

首先,我们打开NodeJS官网,下载NodeJS安装包。

下载完成后双击打开,并一路下一步安装。

安装完成后,打开命令提示符,试试node命令和npm命令是否正常。

看到如图类似的内容,证明你的nodenpm都已经安装成功了。

搭建小程序开发环境

开发小程序的第一步,你需要拥有一个小程序帐号,通过这个帐号你就可以管理你的小程序。

申请账号

点击 https://mp.weixin.qq.com/wxopen/waregister?action=step1 根据指引填写信息和提交相应的资料,就可以拥有自己的小程序帐号。

如果你注册过小程序,可以点击右侧的立即登录。如果没有的话,请重新注册,值得注意的是,邮箱必须填写未在微信公众平台、未在微信开放平台、个人未绑定的邮箱,不然这里是无法注册的。密码请填写你能记住的密码即可。

现在登录https://mp.weixin.qq.com/,点击左侧的设置——开发设置,在这里,我们就能看到你小程序的AppID了。

当小程序的ID拿到之后,我们就可以下载安装开发工具了。

安装开发工具

现在,打开 开发者工具下载页面 ,根据自己的操作系统下载对应的安装包进行安装。

我这里使用的是Windows 64位操作系统,所以我点击Windwos 64位按钮进行下载。下载完成后,右键,然后以管理员身份运行安装文件。

之后,一路点击下一步安装即可。

接下来,就可以开始运行开发者工具了。使用前需要我们扫描二维码才能开始使用,请打开微信,然后点击发现——扫一扫,扫描开发者工具展示的二维码,之后在手机上点击登录即可。

人脸识别API申请

如果要使用人脸识别API,必须在腾讯云进行实名认证,实名认证后,您可以登录腾讯云 控制台进行使用。如果没有账号,请参考账号 注册教程。注册完成后,需要创建相关开发密钥,不然无法使用API。

您需要在 访问管理 创建密钥,点击图中的新建密钥,即可创建密钥。

创建完成后,点击SecretKey的显示按钮,显示当前SecretKey,然后将APPIDSecretIdSecretKey记录下了,后面教程中使用。

现在,开发小程序所需要的所有环境就已经搭建完成,我们可以开始创建一个新的项目了。

创建云开发项目

首先,我们新建一个云开发的项目,注意AppID是你自己在小程序AppID,同时不要勾选建立云开发模版。

接下来,我们新建两个目录,一个目录(client)存放小程序的客户端,一个目录(server)存放小程序云开发的服务端,如图。

接下来,打开配置文件project.config.json,我们需要新增两行文件。

"cloudfunctionRoot": "server/",
"miniprogramRoot": "client/",

cloudfunctionRoot参数填写你新建的云开发服务器的文件目录,miniprogramRoot填写你小程序客户端的目录,如图。

当你的server文件夹图标变成了☁的样式,证明我们云开发环境就搭建完成了。

项目开发思考

在开始写代码之前,我们先理一下思路。什么东西放在服务端,什么东西放在客户端?

从安全角度考虑,我们在腾讯云申请到的API密钥是不能暴漏的,否则别人可以通过抓包去获取我们的ID,从而滥用造成经济上的损失,接下来就是为了识别人脸而上传的图片文件,用户数据十分重要,图片千万不能暴漏。小程序官方也提供了一些如数据库、存储、云函数所相关的能力,我们可以通云开发提供的云函数能力将腾讯的API密钥存放在服务端运行,同样的,也可以使用期所提供的云存储和数据库存放用户的图片及数据。

从产品开发角度考虑,希望产品运行足够的快,减少客户端与服务器的通讯次数,降低运维压力,增加并发数,同时,也要考虑到后期维护,所以代码尽可能的精简。

具体思路是这样子的: 客户端选择完图片,然后在小程序端调用云存储上传API上传图片到云存储,之后由云存储返回一个文件的ID到客户端。 客户端获取文件上传后的ID,调用云函数,在云函数端去读取云存储的文件,读取其真实的URL地址。 将获取到的地址在云函数端发送至腾讯云人脸识别API,等待人脸识别接口返回相关内容。 人脸识别API返回内容后,云函数原封不动的将数据发回给客户端。 客户端做解析,并展示给前端。

整个过程云函数只与客户端通讯两次,同时将人脸识别API调用及用户图片存放在服务端,保证密钥及资料的安全,能够达到我们的要求。

  • 对于云存储的使用,我在如何进行小程序云存储开发有相关的讲解,请参阅。云存储可以在小程序的客户端调用,也可以在云函数的服务端调用。项目架构中,我们在客户端上传了相关文件,之后获取URL地址等操作均是在服务端完成的。

服务端开发

首先,我们先开发服务端,因为服务端作为架构的中心枢纽,负责接收和发送数据,非常重要。当开发完服务端,撰写客户端数据处理的时候,才能事半功倍。

新建云函数

接下来,我们开始新建云函数,在server文件夹上面点击右键,选择新建NodeJS云函数,然后输入你要建立云函数的名称,我这里命名为Face_Detection

新建完成后,系统会自动生成index.jspackage.json文件。其中index.js是官方给出的Demo文件,我们将生成的代码稍微处理下,只保留最基本的模版。

云函数 - index.js

// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {

}

腾讯云人脸识别API

我们打开腾讯云人脸识别的官网,先看看官方文档的API怎么调用。

我们看到API概览中有很多人脸相关的接口,因为我们只做最基本的人脸检测,所以,选择人脸检测与分析相关接口

点击人脸检测与分析DetectFace接口,查看API文档。我们看到接口提供了人脸(Face)的位置、相应的面部属性和人脸质量信息,位置包括 (x,y,w,h),面部属性包括性别(gender)、年龄(age)、表情(expression)、魅力(beauty)、眼镜(glass)、发型(hair)、口罩(mask)和姿态 (pitch,roll,yaw),人脸质量信息包括整体质量分(score)、模糊分(sharpness)、光照分(brightness)和五官遮挡分(completeness)等信息的识别,那么我们的小程序所返回的相关数据也逃不出这几个内容,所以,给用户展示的信息建议也从这里面的参数中选取。向下拉,查看请求相关参数。

请求参数如图,必填的是ActionVersion,分别对应接口名称及版本号,按照文档,我们这里选择DetectFace2018-03-01。其他数据是选填的,如MaxFaceNum人脸数,Url图片的地址等。我们项目架构中由云存储所分享的是图片的地址,所以URL参数是必要的。同时我们想获取图片的人脸属性信息,所以按照表内容的内容,NeedFaceAttributes参数也是必要的。

输入参数看完了,我们看看输出参数。

输出参数有4组,分别是图片的宽高,人脸的信息以及请求的ID。当然,最重要的还是我们的FaceInfos参数,我看点击蓝色的FaceInfos,看看具体有什么内容。

数据蛮多的,这里的FaceAttributesInfo参数我们会用到,因为在输入参数中,我们需要NeedFaceAttributes,所以当NeedFaceAttributes参数等于1的时候,FaceAttributesInfo参数才会返回相关的数据,我们点开FaceAttributesInfo参数看看。

这里就是返回参数的具体数据了,有性别,年龄,微笑程度等信息,具体大家请看描述。我们看到还有一个Hair参数,是头发的长度、刘海、发色等信息,点开FaceHairAttributesInfo看看。

这里就是该接口的相关信息及所有的参数了,心里大概有个底就可以了。

腾讯云人脸识别SDK

继续向下看文档,我们发现,API中给我们提供了相关的SDK。因为我们客户端的代码是NodeJS的,官方也提供了相关的SDK,那么就直接使用吧!

咦,等下,API Explorer是什么,点击去看看,原来,这是官方给我们提供的一个GUI页面,通过简单的设置,即可生成API调用的代码,能显著降低使用云 API 的难度。

我们就用这个工具来生成我们NodeJS端的代码吧!(注:当然,这里也可自己写相关代码去调用API,但是文档中对签名验证这块讲的模糊不清,所以我还是打算使用SDK)

点开API Explorer工具,我们看到一个页面,我们看到框内有我们前面所准备的SecretIdSecretKey,同时也有前文中我们提到的输入参数UrlNeedFaceAttributes等。等下,必填ActionVersion参数去哪里了?

原来,当我们进入人脸识别的页面后,系统已经自动帮我们填写好了ActionVersion参数,我们只需要直接使用就行了。

我们填写下SecretIdSecretKey这两个参数,然后Url参数中,我们填入一张人脸的图片,这里大家可以自己去搜索下,然后将图片的地址粘贴到这里就可以。最后,将NeedFaceAttributes参数为1,根据文档,我们要返回人脸属性。Region参数大区可以不填,文档中已经说明。

如图,当填写完后,我们点击代码生成,然后选择NodeJS,系统会自动生成我们所需要的代码。(这里有个BUG,URL地址不识别冒号,希望官方修复)

我们点击右侧的在线调用,然后点击发送请求,看看是否返回了正常的数据。

如图,这样的数据就是正常的响应结果。如果显示说您未开通人脸识别服务,请前往这里开启。我们回到代码生成页面,复制NodeJS环境的所有代码。

const tencentcloud = require("../../../../tencentcloud-sdk-nodejs");

const IaiClient = tencentcloud.iai.v20180301.Client;
const models = tencentcloud.iai.v20180301.Models;

const Credential = tencentcloud.common.Credential;
const ClientProfile = tencentcloud.common.ClientProfile;
const HttpProfile = tencentcloud.common.HttpProfile;

let cred = new Credential("AKIDypUyGMo2czFdu0La5NSK0UlpiPtEAuLa", "BAxXw99wa5OUOJ3bw52mPq57wa2HKAoG");
let httpProfile = new HttpProfile();
httpProfile.endpoint = "iai.tencentcloudapi.com";
let clientProfile = new ClientProfile();
clientProfile.httpProfile = httpProfile;
let client = new IaiClient(cred, "", clientProfile);

let req = new models.DetectFaceRequest();

let params = '{"Url":"https://img.ltn.com.tw/Upload/liveNews/BigPic/600_phprquLjl.jpg","NeedFaceAttributes":1}'
req.from_json_string(params);


client.DetectFace(req, function(errMsg, response) {

    if (errMsg) {
        console.log(errMsg);
        return;
    }

    console.log(response.to_json_string());
});

我们分析下代码,第一行代码是引入名为tencentcloud-sdk-nodejs的包文件,这个是腾讯云SDK所依赖的文件。后面几行代码就是声明人脸识别的相关版本,定义HTTP请求等。之后的cred是声明我们的在腾讯云的相关密钥,注意你使用的时候改成自己的,httpProfile.endpoint是我们的接口地址,接下来是重点,params参数是我们人人脸识别特有的接口,后续要增加或删除相关参数都在这里操作。最后的内容是回调函数,并通过console.log控制台输出,那么如何在小程序端调用呢?

我们稍微修改下代码。

云函数 - index.js

const cloud = require('wx-server-sdk')// 云函数入口文件
const tencentcloud = require("tencentcloud-sdk-nodejs"); //腾讯云API 3.0 SDK

cloud.init() // 云开发初始化

var synDetectFace = function (url) { //调用人脸识别API函数
  const IaiClient = tencentcloud.iai.v20180301.Client; //API版本
  const models = tencentcloud.iai.v20180301.Models; //API版本

  const Credential = tencentcloud.common.Credential;
  const ClientProfile = tencentcloud.common.ClientProfile;
  const HttpProfile = tencentcloud.common.HttpProfile;

  let cred = new Credential("AKIDypUyGMo2czFdu0La5NSK0UlpiPtEAuLa", "BAxXw99wa5OUOJ3bw52mPq57wa2HKAoG"); //腾讯云的SecretId和SecretKey
  let httpProfile = new HttpProfile();
  httpProfile.endpoint = "iai.tencentcloudapi.com"; //腾讯云人脸识别API接口
  let clientProfile = new ClientProfile();
  clientProfile.httpProfile = httpProfile;
  let client = new IaiClient(cred, "", clientProfile);

  let req = new models.DetectFaceRequest();

  let params = '{"Url":"https://img.ltn.com.tw/Upload/liveNews/BigPic/600_phprquLjl.jpg","NeedFaceAttributes":1}'
  req.from_json_string(params);

  client.DetectFace(req, function (errMsg, response) {

    if (errMsg) {
      console.log(errMsg);
      return;
    }

    console.log(response.to_json_string());
  });
}

synDetectFace();
// 云函数入口函数
exports.main = async (event, context) => {

}

这里,我们将官方所给的代码封装成名为synDetectFace的函数,然后在最后通过synDetectFace()去调用这个函数。exports.main暂时留空,我们先做第一步测试。注意删掉tencentcloud参数中多余的../../../../因为后面我们将用npm包管理器安装这个依赖,无需多余的../../../../

接下来,我们需要安装相关的依赖文件,因为不管是运行云开发还是运行腾讯云SDK都需要相关的依赖文件,这里,我们就需要用到NodeJS运行环境和npm包管理器了。在我们的云函数目录上面右键,选择在终端中打开

然后输入下面的命令,通过npm包管理器,安装云函数和腾讯云SDK的依赖文件。

npm install tencentcloud-sdk-nodejs --save
npm install wx-server-sdk --save

这两行命令即可安装我们需要的依赖文件,如图所示。

不要关闭这个窗口,我们对我们的人脸识别接口进行测试,看看能否正常返回数据,运行下面的命令。

node index.js

如果看到类似的内容,就证明你的代码配置没有错误,可以进行下一步了。

云存储API调用

根据上面的架构,我们在服务端获取到文件的ID后,使用文件ID去云存储文件的URL地址,目前我们在云存储端还没有文件。那么,第一步,将文件上传到云存储。还好,云开发控制台可以直接上传文件,打开控制台,点击存储管理,如图。

我们在这里随便上传一个文件,建议上传一张人脸的照片,已方便我们后续测试。上传之后点击右侧的复制按钮,复制文件的ID。

接下来,我们打开小程序云存储上传文档,调用getTempFileURL接口,然后将ID传送给云存储。等待云存储返回URL地址,这里我们直接复制官方文档给出的代码。

const cloud = require('wx-server-sdk')

exports.main = async (event, context) => {
  const fileList = ['cloud://xxx', 'cloud://yyy']
  const result = await cloud.getTempFileURL({
    fileList,
  })
  return result.fileList
}

将代码中的cloud://xxx更换为刚才复制的文件ID。

const fileList = ['cloud://test-f97abe.7465-test-f97abe/demo.jpg']

现在,打开我们云函数的index.js文件,将上面的代码整合到其中,并将返回的内容result.fileList打印到控制台。

云函数 - index.js

const cloud = require('wx-server-sdk')// 云函数入口文件
const tencentcloud = require("tencentcloud-sdk-nodejs"); //腾讯云API 3.0 SDK

cloud.init() // 云开发初始化

var synDetectFace = function (url) { //调用人脸识别API函数
  const IaiClient = tencentcloud.iai.v20180301.Client; //API版本
  const models = tencentcloud.iai.v20180301.Models; //API版本

  const Credential = tencentcloud.common.Credential;
  const ClientProfile = tencentcloud.common.ClientProfile;
  const HttpProfile = tencentcloud.common.HttpProfile;

  let cred = new Credential("AKIDypUyGMo2czFdu0La5NSK0UlpiPtEAuLa", "BAxXw99wa5OUOJ3bw52mPq57wa2HKAoG"); //腾讯云的SecretId和SecretKey
  let httpProfile = new HttpProfile();
  httpProfile.endpoint = "iai.tencentcloudapi.com"; //腾讯云人脸识别API接口
  let clientProfile = new ClientProfile();
  clientProfile.httpProfile = httpProfile;
  let client = new IaiClient(cred, "", clientProfile);

  let req = new models.DetectFaceRequest();

  let params = '{"Url":"https://img.ltn.com.tw/Upload/liveNews/BigPic/600_phprquLjl.jpg","NeedFaceAttributes":1}'
  req.from_json_string(params);

  client.DetectFace(req, function (errMsg, response) {

    if (errMsg) {
      console.log(errMsg);
      return;
    }

    console.log(response.to_json_string());
  });
}

synDetectFace();
// 云函数入口函数
exports.main = async (event, context) => {
  const fileList = ['cloud://test-f97abe.7465-test-f97abe/demo.jpg']
  const result = await cloud.getTempFileURL({
    fileList,
  })
  return result.fileList
}

接下来,我们部署下云函数,让其在云开发端运行,我们看看能不能正常读取到我们所需要的文件。在云函数上右键,选择上传并部署:所有文件,这一步,我们将我们刚刚写的代码及所需要的依赖环境部署在服务端。

当弹出的对话框显示上传并部署完成后,我们就可以打开云开发的控制台进行测试了。

点击云函数按钮,选择我们刚刚上传的云函数,然后单击右侧的测试。

当点击运行测试按钮后,查看当前返回结果,如果显示成功,返回结果内有你提交的文件ID及文件的URL地址,证明我们云存储在服务端的调用执行成功。

云函数代码整合

既然腾讯云人脸API和云函数端的云存储API已经调用并测试成功,那么就需要我们整合下两个代码,因为现在咱们服务端的代码是分离的,况且代码中传入的图片也不是我们想要的图片。

云函数 - index.js(最终版)

const cloud = require('wx-server-sdk') //小程序云开发SDK
const tencentcloud = require("tencentcloud-sdk-nodejs"); //腾讯云API 3.0 SDK
cloud.init() //云开发初始化
var synDetectFace = function(url) { //人脸识别API
  const IaiClient = tencentcloud.iai.v20180301.Client; //API版本
  const models = tencentcloud.iai.v20180301.Models; //API版本

  const Credential = tencentcloud.common.Credential;
  const ClientProfile = tencentcloud.common.ClientProfile;
  const HttpProfile = tencentcloud.common.HttpProfile;
  let cred = new Credential("AKIDypUyGMo2czFdu0La5NSK0UlpiPtEAuLa", "BAxXw99wa5OUOJ3bw52mPq57wa2HKAoG"); //腾讯云的SecretId和SecretKey
  let httpProfile = new HttpProfile();
  httpProfile.endpoint = "iai.tencentcloudapi.com"; //腾讯云人脸识别API接口
  let clientProfile = new ClientProfile();
  clientProfile.httpProfile = httpProfile;
  let client = new IaiClient(cred, "", clientProfile); //调用就近地域

  let req = new models.DetectFaceRequest();
  let params = '{"Url":"' + url + '","NeedFaceAttributes":1}' //拼接参数
  req.from_json_string(params);
  return new Promise(function(resolve, reject) { //构造异步函数
    client.DetectFace(req, function(errMsg, response) {
      if (errMsg) {
        reject(errMsg)
      } else {
        resolve(response);
      }
    })
  })
}


exports.main = async(event, context) => {
  const data = event
  const fileList = [data.fileID] //读取来自客户端的fileID
  const result = await cloud.getTempFileURL({
    fileList, //向云存储发起读取文件临时地址请求
  })
  const url = result.fileList[0].tempFileURL
  datas = await synDetectFace(url) //调用异步函数,向腾讯云API发起请求
  return datas
}

我们将两个代码进行了整合,并增加了相关的备注。

首先,将腾讯云人脸识别API整体封装成为一个名为synDetectFace异步函数,该函数携带名为url的变量,当调用函数的时候,我们传入url参数,函数会通过Promise方式将人脸识别返回的内容重新返回给调用端。

接下来,为了方便云函数的调用,我们将客户端传过来的内容(文件ID)存为变量data,并向云存储发起URL请求,将请求的返回值传到异步函数synDetectFace(url),此时,该函数会向腾讯云发起AI识别请求,返回的请求值最终会返回给客户端。

修改完代码,别忘了部署在服务端。到这一步,我们服务端的开发工作就全部搞定了。

客户端开发

服务端开发完成后,我们已经完成了三分之二的代码,接下来就是撰写客户端的代码。关掉server文件夹。打开client文件夹,然后新建一个名为app.json的文件,如图。

将下面的代码复制到app.json文件中。

{
  "pages": [
    "pages/index/index"
  ],
  "window": {
    "navigationBarBackgroundColor": "#ffffff",
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "人脸识别Demo",
    "backgroundColor": "#eeeeee",
    "backgroundTextStyle": "light",
    "enablePullDownRefresh": false
  },
  "cloud": true
}

保存完成后,系统将自动生成相关目录及文件。

选择图片API

根据流程,我们的第一步就是选择图片了,小程序官方也提供了图片选择API,废话不多说,我们直接看代码。首先,打开index.js文件,注意,这里选择的是客户端的文件,不是服务端的。

客户端 - index.js

// pages/index/index.js
Page({

  /**
   * 页面的初始数据
   */
  data: {

  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {

  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})

同样,官方也提供了一堆已经存在的函数,为了方便我们撰写代码,我们删除用不到的内容。

客户端 - index.js

Page({
  data: {
  },
  onLoad: function (options) {
  }
})

data存放我们一些静态数据,以便前端调用,onLoad是小程序的初始化执行函数,当小程序页面加载成功后会自动调用该函数,我们后续会用到。

接下来我们新建一个函数,这个函数用于当用户在前端点击某个按钮的时候,会执行该函数,我们将其命名为UploadImage

客户端 - index.js

Page({
  data: {

  },
  UploadImage(){

  },
  onLoad: function (options) {

  }
})

参考小程序官方的文档中的示例代码,修改代码如下。

客户端 - index.js

Page({
  data: {

  },
  UploadImage(){
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths
        console.log(tempFilePaths)
      }
    })
  },
  onLoad: function (options) {

  }
})

参考官方的文档,参数中,我们定一个图片的张数,图片的尺寸、来源。之后该API回调返回tempFilePaths临时图片地址。

后端函数写完了,我们需要点击一个按钮执行上传,撰写下前端,打开index.wxml文件,修改代码如下。

客户端 - index.wxml

 <button type="primary" bindtap="UploadImage">上传照片</button>

当用户点击图中的上传照片按钮的时候,会自动调用我们在后端撰写的UploadImage函数,如图。

当我们选择照片后,会在控制台输出当前文件的临时地址,有了临时地址,接下来一步就是将临时地址传给云存储的API,让其将文件上传。

云存储上传文件API

同选择图片API一样,微信官方文档中也提供了相关的实例代码。我们可以直接使用,先看代码。

客户端 - index.js

Page({
  data: {

  },
  UploadImage() {
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths[0]
        console.log(tempFilePaths)
        wx.cloud.uploadFile({
          cloudPath: 'example.jpg',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
          },
          fail: err => {
          }
        })
      }
    })
  },
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-f97abe'
    })
  }
})

当上传图片API返回tempFilePaths参数后,我们需要将其传入云存储上传文件的目录。,根据文档,filePath参数就是文件资源的目录。我们直接将返回tempFilePaths参数放在该参数下,当选择完图片,会自动调用该API上传名为example.jpg的图片文件,同时我们上传的文件只有一张,也就是临时目录的第一张,所以返回的参数应改为res.tempFilePaths[0]

根据文档要求,在调用云开发之前,我们还必须初始化,才能正常使用。所以代码中我们调用了wx.cloud.init方法,并填写了env参数,这里的参数请改为你自己的,打开云开发控制台可查看到你的环境ID

上传成功后,返回回调内容,并在控制台打印出其文件ID。

控制台第一行是我们选择图片后的临时地址,第二行上传到云存储后的文件ID。

调用云函数API

当云存储调用完成后,我们拿到了文件的ID,下一步就是真正的调用云函数了,我们将文件ID传给云函数,并等待云函数返回人脸识别的结果。看一下官方的文档

我们必须填写的有云函数的名字,选填的有dataconfig,由于我们不更改局部配置文件,所以config用不到。但是我们需要将我们的文件ID传送给服务端,所以要填写的还有data参数。

参考官方代码,我们调用下上面撰写的Face_Detection云函数。

客户端 - index.js

Page({
  data: {

  },
  UploadImage() {
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths[0]
        console.log(tempFilePaths)
        wx.cloud.uploadFile({
          cloudPath: 'example.jpg',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
            wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                console.log(res.result)
              },
            })
          },
          fail: err => {
          }
        })
      }
    })
  },
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-f97abe'
    })
  }
})

这里的代码逻辑是,当我们图片上传完成后,通过返回的文件ID去调用名为Face_Detection的云函数,该函数在服务端进行一系列操作后,将数据返回给客户端,最终通过控制台打印出来。

如果看到类似的结果,证明客户端基本的请求操作已经写完,这里返回的内容就是我们人脸识别后腾讯云API所返回的数据,我们可以进行下一步操作了。

当然,这里代码中有一个BUG,就是所有上传的图片名称都是example.jpg,一个人使用当然不会造成什么问题,但如果多个人并发使用这个小程序,就会产生一个很大的BUG,同一时间内请求的图片地址相同,那么返回的结果也相同,更有可能造成云存储系统BUG。

为此,我将图片的名称变更为一个随机数,这个同一时间内随机数的内容不同,那么图片请求的时候就不会有多大问题,修改代码如下。

客户端 - index.js

Page({
  data: {

  },
  UploadImage() {
    var random = Date.parse(new Date()) + Math.ceil(Math.random() * 1000)
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths[0]
        console.log(tempFilePaths)
        wx.cloud.uploadFile({
          cloudPath: random + '.png',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
            wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                console.log(res.result)
              },
            })
          },
          fail: err => {
          }
        })
      }
    })
  },
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-f97abe'
    })
  }
})

我们新增了一个名为random的随机数,这个随机数是当前的UTC时间与4位随机数相加而得到的一个结果,然后将这个随机数存为我们的图片文件的名称。这样写的好处是,在同1秒内,最多可以支持1000张图片的并发。

返回值优化

接下来,我们将返回的数据展示在前端,先随便找一个参数返回在前端。先简单测试下,我们将部分数据输出到控制台。首先,我们看看腾讯云人脸识别返回的参数列表。

简单点,我们返回人脸Beauty参数到控制台吧,参考人脸识别返回的json数据,修改上面代码中的console.log(res.result)返回参数。

console.log(res.result.FaceInfos[0].FaceAttributesInfo.Beauty)

再次上传一遍数据,看看返回结果变成什么了。

这里返回的就是当前的魅力值,接下来,我们将这里的魅力数据传到前端。我们可以使用官方的setData方法。修改整体代码如下。

客户端 - index.js

Page({
  data: {
    Beauty:""
  },
  UploadImage() {
    var random = Date.parse(new Date()) + Math.ceil(Math.random() * 1000)
    var myThis = this
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths[0]
        console.log(tempFilePaths)
        wx.cloud.uploadFile({
          cloudPath: random + '.png',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
            wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                console.log(res.result.FaceInfos[0].FaceAttributesInfo.Beauty)
                myThis.setData({
                  Beauty: res.result.FaceInfos[0].FaceAttributesInfo.Beauty
                })
              },
            })
          },
          fail: err => {
          }
        })
      }
    })
  },
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-f97abe'
    })
  }
})

客户端 - index.wxml

 <button type="primary" bindtap="UploadImage">上传照片</button>
 <text>{{Beauty}}</text>

我们在data中,新增一个Beauty变量,然后在下面的UploadImage函数中,使用myThis.setData函数改变变量Beauty数据。最后,我们在index.wxml文件中去显示这个返回的内容。

当然,返回的一个参数没有多大用,我们将后台的所有数据都返回到前端,并参考腾讯云官方的文档,优化下首页显示。

客户端 - index.js

Page({
  data: {
    age: "请上传照片",
    glasses: "请上传照片",
    beauty: "请上传照片",
    mask: "请上传照片",
    hat: "请上传照片",
    gender: "请上传照片",
    hair_length: "请上传照片",
    hair_bang: "请上传照片",
    hair_color: "请上传照片",
  },
  UploadImage() {
    var random = Date.parse(new Date()) + Math.ceil(Math.random() * 1000)
    var myThis = this
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths[0]
        console.log(tempFilePaths)
        wx.cloud.uploadFile({
          cloudPath: random + '.png',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
            wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                myThis.setData({
                  age: res.result.FaceInfos[0].FaceAttributesInfo.Age,
                  glasses: res.result.FaceInfos[0].FaceAttributesInfo.Glass,
                  beauty: res.result.FaceInfos[0].FaceAttributesInfo.Beauty,
                  mask: res.result.FaceInfos[0].FaceAttributesInfo.Mask,
                  hat: res.result.FaceInfos[0].FaceAttributesInfo.Hat,
                  gender:res.result.FaceInfos[0].FaceAttributesInfo.Gender,
                  hair_length: res.result.FaceInfos[0].FaceAttributesInfo.Hair.Length,
                  hair_bang: res.result.FaceInfos[0].FaceAttributesInfo.Hair.Bang,
                  hair_color: res.result.FaceInfos[0].FaceAttributesInfo.Hair.Bang
                })
              },
            })
          },
          fail: err => {
          }
        })
      }
    })
  },
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-f97abe'
    })
  }
})

客户端 - index.wxml

 <button type="primary" bindtap="UploadImage">上传照片</button>
  <text class="text_size">性别:{{gender}}</text>
  <text class="text_size">年龄:{{age}}</text>
  <text class="text_size">颜值:{{beauty}}</text>
  <text class="text_size">是否带眼镜:{{glasses}}</text>
  <text class="text_size">是否有帽子:{{hat}}</text>
  <text class="text_size">是否有口罩:{{mask}}</text>
  <text class="text_size">头发长度:{{hair_length}}</text>
  <text class="text_size">有无刘海:{{hair_bang}}</text>
  <text class="text_size">头发颜色:{{hair_color}}</text>

如图,我们已经将所需要的数据展示在前端了。但是展示的不够完美,很多数据给用户展示用户也无法看懂。比如性别:0头发长度:3有无刘海:1等。参考腾讯云API文档,我们将这里的数据使用switchif语句做下判断,不同的数据返回不同的内容,让用户看明白。

客户端 - index.js

Page({
  data: {
    age: "请上传照片",
    glasses: "请上传照片",
    beauty: "请上传照片",
    mask: "请上传照片",
    hat: "请上传照片",
    gender: "请上传照片",
    hair_length: "请上传照片",
    hair_bang: "请上传照片",
    hair_color: "请上传照片",
  },
  UploadImage() {
    var random = Date.parse(new Date()) + Math.ceil(Math.random() * 1000)
    var myThis = this
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths[0]
        console.log(tempFilePaths)
        wx.cloud.uploadFile({
          cloudPath: random + '.png',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
            wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                myThis.setData({
                  age: res.result.FaceInfos[0].FaceAttributesInfo.Age,
                  glasses: res.result.FaceInfos[0].FaceAttributesInfo.Glass,
                  beauty: res.result.FaceInfos[0].FaceAttributesInfo.Beauty,
                  mask: res.result.FaceInfos[0].FaceAttributesInfo.Mask,
                  hat: res.result.FaceInfos[0].FaceAttributesInfo.Hat,
                })
                if (res.result.FaceInfos[0].FaceAttributesInfo.Gender < 50) {
                  myThis.setData({
                    gender: "女"
                  });
                } else {
                  myThis.setData({
                    gender: "男"
                  });
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Length) {
                  case 0:
                    myThis.setData({
                      hair_length: "光头"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_length: "短发"
                    });
                    break;
                  case 2:
                    myThis.setData({
                      hair_length: "中发"
                    });
                    break;
                  case 3:
                    myThis.setData({
                      hair_length: "长发"
                    });
                    break;
                  case 4:
                    myThis.setData({
                      hair_length: "绑发"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Bang) {
                  case 0:
                    myThis.setData({
                      hair_bang: "有刘海"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_bang: "无刘海"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Color) {
                  case 0:
                    myThis.setData({
                      hair_color: "黑色"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_color: "金色"
                    });
                    break;
                  case 0:
                    myThis.setData({
                      hair_color: "棕色"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_color: "灰白色"
                    });
                    break;
                }
              },
            })
          },
          fail: err => {
          }
        })
      }
    })
  },
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-f97abe'
    })
  }
})

现在,我们已经将返回的内容正常显示在前端了。

交互优化

接下来,我们可以优化前端了。

文本优化

首先,最难看的莫过于这里交错的文字了,这里就需要修改index.wxss文件。

客户端 - index.wxss

.text_viwe_size{
  padding-top: 20px;
  padding-bottom: 30px;
  padding-left: 30px;
  padding-right: 30px;
}
.text_size{
  display:flex;
  color: #444444;
}

我们定义一下文本显示框的大小,并定义文本显示方式和色彩。然后,修改index.wxml文件,让其引用index.wxss文件。

客户端 - index.wxml

 <button type="primary" bindtap="UploadImage">上传照片</button>
  <view class='text_viwe_size'>
  <text class="text_size">性别:{{gender}}</text>
  <text class="text_size">年龄:{{age}}</text>
  <text class="text_size">颜值:{{beauty}}</text>
  <text class="text_size">是否带眼镜:{{glasses}}</text>
  <text class="text_size">是否有帽子:{{hat}}</text>
  <text class="text_size">是否有口罩:{{mask}}</text>
  <text class="text_size">头发长度:{{hair_length}}</text>
  <text class="text_size">有无刘海:{{hair_bang}}</text>
  <text class="text_size">头发颜色:{{hair_color}}</text>
  </view>

上传进度

参考小程序官方的文档,调用progress组件,我们新增一个进度条组件,打开index.wxml文件,先在前端显示相关内容。

客户端 - index.wxml

<button type="primary" bindtap="UploadImage">上传照片</button>
<view class='progress_view_size'>
  <progress percent="{{progress}}" show-info />
  <text class="text_size">上传进度</text>
</view>
<view class='text_viwe_size'>
  <text class="text_size">性别:{{gender}}</text>
  <text class="text_size">年龄:{{age}}</text>
  <text class="text_size">颜值:{{beauty}}</text>
  <text class="text_size">是否带眼镜:{{glasses}}</text>
  <text class="text_size">是否有帽子:{{hat}}</text>
  <text class="text_size">是否有口罩:{{mask}}</text>
  <text class="text_size">头发长度:{{hair_length}}</text>
  <text class="text_size">有无刘海:{{hair_bang}}</text>
  <text class="text_size">头发颜色:{{hair_color}}</text>
</view>

当然,前端展示了还无法显示当前进度,我们需要在后端调用返回相关进度,然后再向前端展示。

客户端 - index.js

Page({
  data: {
    age: "请上传照片",
    glasses: "请上传照片",
    beauty: "请上传照片",
    mask: "请上传照片",
    hat: "请上传照片",
    gender: "请上传照片",
    hair_length: "请上传照片",
    hair_bang: "请上传照片",
    hair_color: "请上传照片",
  },
  UploadImage() {
    var random = Date.parse(new Date()) + Math.ceil(Math.random() * 1000)
    var myThis = this
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths[0]
        console.log(tempFilePaths)
        var uploadTask =  wx.cloud.uploadFile({
          cloudPath: random + '.png',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
            wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                myThis.setData({
                  age: res.result.FaceInfos[0].FaceAttributesInfo.Age,
                  glasses: res.result.FaceInfos[0].FaceAttributesInfo.Glass,
                  beauty: res.result.FaceInfos[0].FaceAttributesInfo.Beauty,
                  mask: res.result.FaceInfos[0].FaceAttributesInfo.Mask,
                  hat: res.result.FaceInfos[0].FaceAttributesInfo.Hat,
                })
                if (res.result.FaceInfos[0].FaceAttributesInfo.Gender < 50) {
                  myThis.setData({
                    gender: "女"
                  });
                } else {
                  myThis.setData({
                    gender: "男"
                  });
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Length) {
                  case 0:
                    myThis.setData({
                      hair_length: "光头"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_length: "短发"
                    });
                    break;
                  case 2:
                    myThis.setData({
                      hair_length: "中发"
                    });
                    break;
                  case 3:
                    myThis.setData({
                      hair_length: "长发"
                    });
                    break;
                  case 4:
                    myThis.setData({
                      hair_length: "绑发"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Bang) {
                  case 0:
                    myThis.setData({
                      hair_bang: "有刘海"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_bang: "无刘海"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Color) {
                  case 0:
                    myThis.setData({
                      hair_color: "黑色"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_color: "金色"
                    });
                    break;
                  case 0:
                    myThis.setData({
                      hair_color: "棕色"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_color: "灰白色"
                    });
                    break;
                }
              },
            })
          },
          fail: err => {
          }
        })
        uploadTask.onProgressUpdate((res) => {
          myThis.setData({
            progress: res.progress //上传进度
          })
        })
      }
    })
  },
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-f97abe'
    })
  }
})

这里的代码中,我们将wx.cloud.uploadFile存为名为uploadTask的变量,然后在最后调用onProgressUpdate(res)方法,并通过setData方法,将数据展示在前端。接下来,优化下wxss的代码,将前端修改漂亮点。

客户端 - index.wxss

.text_viwe_size{
  padding-top: 20px;
  padding-bottom: 30px;
  padding-left: 30px;
  padding-right: 30px;
}
.text_size{
  display:flex;
  color: #444444;
}
.progress_view_size{
  padding-left: 30px;
  padding-right: 30px;
}

显示识别图片

现在展示端还不够完美,人脸识别,当然是要将用户的照片展示在前端,我们在index.wxml文件中插入图片。

客户端 - index.wxml

<view class="image_viwe_size">
<image src="{{image_src}}" style="width: 200px; height: 200px;"></image>
</view>
<view class='button_viwe_size'>
  <button class="button_size" type="primary" bindtap="UploadImage">上传照片</button>
</view>
<view class='progress_view_size'>
  <progress percent="{{progress}}" show-info />
  <text class="text_size">上传进度</text>
</view>
<view class='text_viwe_size'>
  <text class="text_size">性别:{{gender}}</text>
  <text class="text_size">年龄:{{age}}</text>
  <text class="text_size">颜值:{{beauty}}</text>
  <text class="text_size">是否带眼镜:{{glasses}}</text>
  <text class="text_size">是否有帽子:{{hat}}</text>
  <text class="text_size">是否有口罩:{{mask}}</text>
  <text class="text_size">头发长度:{{hair_length}}</text>
  <text class="text_size">有无刘海:{{hair_bang}}</text>
  <text class="text_size">头发颜色:{{hair_color}}</text>
</view>

然后,我们需要在后端去写image_src图片地址,做展示,打开index.js文件,在data中指定图片地址。同时,当用户点击图片上传按钮的时候,我们将图片地址替换为上传的临时文件。

客户端 - index.js

Page({
  data: {
    age: "请上传照片",
    glasses: "请上传照片",
    beauty: "请上传照片",
    mask: "请上传照片",
    hat: "请上传照片",
    gender: "请上传照片",
    hair_length: "请上传照片",
    hair_bang: "请上传照片",
    hair_color: "请上传照片",
    image_src:"https://cdn-img.easyicon.net/image/2019/panda-index.svg"
  },
  UploadImage() {
    var random = Date.parse(new Date()) + Math.ceil(Math.random() * 1000)
    var myThis = this
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths[0]
        console.log(tempFilePaths)
        myThis.setData({
          image_src: res.tempFilePaths[0]
        });
        var uploadTask =  wx.cloud.uploadFile({
          cloudPath: random + '.png',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
            wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                myThis.setData({
                  age: res.result.FaceInfos[0].FaceAttributesInfo.Age,
                  glasses: res.result.FaceInfos[0].FaceAttributesInfo.Glass,
                  beauty: res.result.FaceInfos[0].FaceAttributesInfo.Beauty,
                  mask: res.result.FaceInfos[0].FaceAttributesInfo.Mask,
                  hat: res.result.FaceInfos[0].FaceAttributesInfo.Hat,
                })
                if (res.result.FaceInfos[0].FaceAttributesInfo.Gender < 50) {
                  myThis.setData({
                    gender: "女"
                  });
                } else {
                  myThis.setData({
                    gender: "男"
                  });
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Length) {
                  case 0:
                    myThis.setData({
                      hair_length: "光头"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_length: "短发"
                    });
                    break;
                  case 2:
                    myThis.setData({
                      hair_length: "中发"
                    });
                    break;
                  case 3:
                    myThis.setData({
                      hair_length: "长发"
                    });
                    break;
                  case 4:
                    myThis.setData({
                      hair_length: "绑发"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Bang) {
                  case 0:
                    myThis.setData({
                      hair_bang: "有刘海"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_bang: "无刘海"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Color) {
                  case 0:
                    myThis.setData({
                      hair_color: "黑色"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_color: "金色"
                    });
                    break;
                  case 0:
                    myThis.setData({
                      hair_color: "棕色"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_color: "灰白色"
                    });
                    break;
                }
              },
            })
          },
          fail: err => {
          }
        })
        uploadTask.onProgressUpdate((res) => {
          myThis.setData({
            progress: res.progress //上传进度
          })
        })
      }
    })
  },
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-f97abe'
    })
  }
})

最后,优化下前端的index.wxss文件。

客户端 - index.wxss

.image_viwe_size{
  display:flex;
  justify-content: center;
  background: #FFFFFF;
}
.image_size{
  width: 300px;
}
.button_viwe_size{
  display:flex;
  padding-top: 10px;
  padding-bottom: 10px;
}
.button_size{
  height:50px;
  width: 200px;
}
.text_viwe_size{
  padding-top: 20px;
  padding-bottom: 30px;
  padding-left: 30px;
  padding-right: 30px;
}
.text_size{
  display:flex;
  color: #444444;
}
.progress_view_size{
  padding-left: 30px;
  padding-right: 30px;
}

现在,整个UI好看多了。

识别状态展示

现在,我们已经有了上传图片进度条,但是用户上传图片后没有相关提示信息给用户,用户也不知道图片上传后是返回结果是不是正常的。那么,怎么去优化这块呢?幸好微信官方给我们提供了交互接口,我们直接调用就行,直接上代码。

客户端 - index.js

Page({
  data: {
    age: "请上传照片",
    glasses: "请上传照片",
    beauty: "请上传照片",
    mask: "请上传照片",
    hat: "请上传照片",
    gender: "请上传照片",
    hair_length: "请上传照片",
    hair_bang: "请上传照片",
    hair_color: "请上传照片",
    image_src:"https://cdn-img.easyicon.net/image/2019/panda-index.svg"
  },
  UploadImage() {
    var random = Date.parse(new Date()) + Math.ceil(Math.random() * 1000)
    var myThis = this
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        wx.showLoading({
          title: '加载中...',
        });
        const tempFilePaths = res.tempFilePaths[0]
        console.log(tempFilePaths)
        myThis.setData({
          image_src: res.tempFilePaths[0]
        });
        var uploadTask =  wx.cloud.uploadFile({
          cloudPath: random + '.png',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
            wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                wx.hideLoading()
                wx.showToast({
                  title: '成功',
                  icon: 'success',
                  duration: 500
                })
                myThis.setData({
                  age: res.result.FaceInfos[0].FaceAttributesInfo.Age,
                  glasses: res.result.FaceInfos[0].FaceAttributesInfo.Glass,
                  beauty: res.result.FaceInfos[0].FaceAttributesInfo.Beauty,
                  mask: res.result.FaceInfos[0].FaceAttributesInfo.Mask,
                  hat: res.result.FaceInfos[0].FaceAttributesInfo.Hat,
                })
                if (res.result.FaceInfos[0].FaceAttributesInfo.Gender < 50) {
                  myThis.setData({
                    gender: "女"
                  });
                } else {
                  myThis.setData({
                    gender: "男"
                  });
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Length) {
                  case 0:
                    myThis.setData({
                      hair_length: "光头"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_length: "短发"
                    });
                    break;
                  case 2:
                    myThis.setData({
                      hair_length: "中发"
                    });
                    break;
                  case 3:
                    myThis.setData({
                      hair_length: "长发"
                    });
                    break;
                  case 4:
                    myThis.setData({
                      hair_length: "绑发"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Bang) {
                  case 0:
                    myThis.setData({
                      hair_bang: "有刘海"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_bang: "无刘海"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Color) {
                  case 0:
                    myThis.setData({
                      hair_color: "黑色"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_color: "金色"
                    });
                    break;
                  case 0:
                    myThis.setData({
                      hair_color: "棕色"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_color: "灰白色"
                    });
                    break;
                }
              },
            })
          },
          fail: err => {
          }
        })
        uploadTask.onProgressUpdate((res) => {
          myThis.setData({
            progress: res.progress //上传进度
          })
        })
      }
    })
  },
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-f97abe'
    })
  }
})

图片文件选择成功后,我们调用wx.showLoading接口,展示加载中的提示框。当云函数回调成功后,我们立刻调用wx.hideLoading隐藏加载中的提示框。同时,我们调用wx.showToast接口,显示消息成功提示框。

现在,我们就完成了一款人脸识别小程序产品的开发,并能够正常展示给用户。

总结

项目终于写完了,你学会了整体的小程序·云开发并通过腾讯云人脸识别流程了吗?希望这篇文章能给你带来一些新的经验和想法!

当然,这里的项目还有一些问题,比如图片上传到云存储后会一直存在,没有清空缓存的机制。比如一秒内用户最大并发是1000,因为图片我们设置的随机数最大是1000,后面建议将随机数改为读取图片的md5值然后显示出来。这些BUG我也会慢慢去优化,喜欢请关注(Star)我的小程序人脸识别项目(https://github.com/Techeek/WX_TencentAI_Face)。

更多云AI产品特惠请关注:腾讯云开春采购节

微信扫码可进入“”腾讯云AI体验中心”小程序,体验多款AI产品

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券