由于种种原因,小程序需要接入微信内容安全检查 Api。最终实现方案有点曲折,还没想到有什么更好的办法。
通过云函数调用
security.imgSecCheck和security.msgSecCheck实现图片和文字违法违规内容检查
实现思路
实现思路:在云函数中调用 微信内容安全检查 Api,图片上传前调用云函数来进行安全检查。
微信内容安全检查 Api 有三个接口:security.imgSecCheck、security.mediaCheckAsync、security.msgSecCheck。
-
security.mediaCheckAsync:可异步校验图片/音频是否含有违法违规内容,需要传入音频或图片url,单个文件大小不超过10M。只支持在服务端调用。 -
security.imgSecCheck、security.msgSecCheck:两个接口分别校验图片和文字,图片大小限制为1M。支持服务端调用和云函数调用。
在开发的小程序场景中,需要检查文字,图片需要在上传前进行校验,并且在服务端改动会比较麻烦。于是选择使用在云函数中调用security.imgSecCheck、security.msgSecCheck。
具体实现
一、云函数实现
cloud = require('wx-server-sdk');
cloud.init();
exports.main = async function (event, context) {
let opts = {}
let fun = '';
if (event.type === 2) { // 文字检查
opts = {
content: event.content || ''
};
fun = cloud.openapi.security.msgSecCheck(opts);
} else {// 图片检查
let imgbase64 = event.imgbase64;
let buffer = Buffer.from(imgbase64, 'base64');
opts = {
media: {
contentType: 'image/jpg',
value: buffer
}
};
fun = cloud.openapi.security.imgSecCheck(opts);
}
return fun.then(res => {
return res;
}).catch(err => {
return err;
});
}
二、小程序代码
//文字信息安全检查
const msgSecCheck = function (content) {
return new Promise((resolve, reject) => {
wx.cloud.callFunction({
name: 'securityCheck',
data: { content: content, type: 2 },
}).then(res => {
if (res && res.result && res.result.errCode === 87014) {
resolve(true);
} else {
resolve(false);
}
}).catch(err => {
//只要检查失败,都算作没有安全风险
resolve(false);
})
})
}
// 图片安全检查
// tempFiles:{path:'',size:''}
const checkImgSafe = function (tempFiles) {
return new Promise((resolve, reject) => {
// 压缩图片
getImg(tempFiles.path).then(tmpPath => {
//先将图片转换为base64,然后调用云函数
const fs = wx.getFileSystemManager();
fs.readFile({
filePath: tmpPath,
encoding: 'base64',
success: function (res) {
wx.cloud.callFunction({
name: 'securityCheck',
data: { imgbase64: res.data },
}).then(res => {
if (res && res.result && res.result.errCode === 87014) {
reject(); //有安全风险
} else {
resolve();
}
}).catch(err => {
//只要检查失败,都算作没有安全风险
resolve();
})
}
});
}).catch(err => {
resolve();
});
});
};
// 图片压缩
const getImg = function (path) {
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: path,
success(res) {
// 图片原尺寸
let height = res.height;
let width = res.width;
let maxWidth = 200;
let maxHeight = 200;
// 压缩后尺寸
let targetWidth = width;
let targetHeight = height;
// 等比例压缩,如果宽度大于高度,则宽度优先,否则高度优先
if (width > maxWidth || height > maxHeight) {
if (width / height > maxWidth / maxHeight) {
// 要求宽度*(原生图片比例)=新图片尺寸
targetWidth = maxWidth;
targetHeight = Math.round(maxWidth * (height / width));
} else {
targetHeight = maxHeight;
targetWidth = Math.round(maxHeight * (width / height));
}
}
const ctx = wx.createCanvasContext("canvas-img");
ctx.clearRect(0, 0, targetWidth, targetHeight);
ctx.drawImage(path, 0, 0, targetWidth, targetHeight);
ctx.draw(true, function () {
wx.canvasToTempFilePath({
canvasId: "canvas-img",
x: 0,
y: 0,
width: targetWidth,
height: targetHeight,
quality: 0.5,
fileType: 'jpg',
success: function (res) {
resolve(res.tempFilePath)
},
fail: function (err) {
reject(err)
}
});
});
},
fail: function (err) {
reject(err);
}
})
})
};
<!-- wxml -->
<canvas canvas-id="canvas-img" style="top:-500;position:fixed;"></canvas>
注意事项:
- 图片需要先压缩:两个原因:
openapi.security.imgSecCheck可检查图片大小宣称1M以下,实测提示信息里写的不能超过5120kb;图片还没有上传就需要检查,只有本地路径,因此无法传url,此处将图片转为base64格式。实测调用云函数时,传输base64200kb+ 的参数就会失败( IDE 没有问题,客户端会报错cloud.callFunction:fail timeout for result fetching)。 canvas不能display:none。这里将canvas元素放到页面外看不到的区域。
tips:代码写的比较挫,不知道是否有更好的办法。