美文网首页
通过security.imgSecCheck接口审核照片并实现多

通过security.imgSecCheck接口审核照片并实现多

作者: 最后的代码 | 来源:发表于2020-03-06 18:39 被阅读0次

如果你打算或正在开发一款用户可以发布图文内容的小程序,那么内容安全问题一定是一个无法跳过的坎。用户多了人工检查照片的工作量问题暂且不提,首先TX的代码审核这一关都不一定过得去。

审核结果

其实关于imgSecCheck的调用,网上已经有不少的教程了,其思路大致都是使用canvas绘出一张缩略图,然后读取其buffer传至云函数,通过云调用来检验照片并返回结果。
其实本人也使用了同样的流程,并没有开拓出新的思路。虽然如此,但其它教程单图上传的案例居多,并且几乎在都使用旧版的canvas API,所以觉得还是可以把自己的代码拿出来交流一下。

准备

在视图层创建canvas节点

<canvas
    id="compress"
    type="2d"
    style="width:{{cWidth}}px; height:{{cHeight}}px"
    >
</canvas>

若不想在屏幕中显示画布,可在style属性中将其设置为绝对定位,并将其位置设置在屏幕范围之外。
比如:
position:absolute; left:-1000px; top:-1000px;

初始化

onLoad: function(options) {
  this.imageList = [];
},
// 根据需求添加代码,此处仅为页面设置了imageList属性用以保存审核通过的照片

核心逻辑

添加照片

API wx.chooseImage

// 绑定页面中“添加照片”按钮的事件
addImage: function () {
    wx.chooseImage({
        count: 3 - this.imageList.length, // 可添加照片的最大数量,根据业务需求更改
        sizeType: ['compressed'],
    }).then(res => {
        this.check(res.tempFilePaths);    // 为简化案例,添加照片后直接开始审核,根据需求自行修改
    })
},

处理照片

为了节约请求时间,调用云函数之前,先将照片进行一次压缩:

  • 获取照片原尺寸
  • 动态计算压缩后的大小
  • 使用canvas进行渲染
  • 保存画布到缓存(临时路径)
  • 读取缓存中的照片buffer
  • 将buffer作为参数传至云函数

由于一些参数会被多次使用,在此,本案例将这些工作封装到了一个函数中,命名为check(array)

用到的API(点击可以查看官方文档)</br>
wx.getImageInfo
wx.createSelectorQuery
wx.canvasToTempFilePath
wx.getFileSystemManager.readFile

wx.cloud.callFunction

check = function (tempFilePaths) {
    let checkList = tempFilePaths;
    let imageList = this.imageList;
    let i = 0; // 用于递归计数

    // 定义函数:调用云函数并检验
    let cloudCheck = (temp, origin) => {
        //... 函数体
    };

    // 定义函数:照片压缩
    let compress = (checkList, i) => {
        let path = checkList[i]; //单张照片的临时路径

        // 定义函数:使用canvas渲染小图
        let render = (path, width, height) => {
            //... 函数体
        };

        // 获取照片比例并计算渲染尺寸
        wx.getImageInfo({
            //... 参数及成功执行后的处理
        });
    };

    compress(checkList, i); 
}

以上代码片段中,我们已经建立好前期处理过程的框架,接下来将各个环节的代码补全。

由于过程中需要用到异步API(不等待API返回结果便执行后面的语句),多张照片不能简单使用循环,需使用递归,即在末尾的then方法或回调函数中调用compress()

获取照片比例并计算渲染尺寸

由于用户上传的照片长宽比例并不统一,不宜将缩略图的尺寸设为固定。建议先读取到照片信息,动态计算canvas画布大小。

案例中将长边设为256px,通过长宽比确定短边。

以下代码在函数compress中。

// 读取照片信息,计算压缩后的大小
wx.getImageInfo({
    src: path // 单张照片的临时路径
}).then(res => {
    let aspectRatio = res.width / res.height;
    let width, height;
    // 本案例限制用户上传的照片比例不超过21: 9,根据需求修改或移除限制
    if (aspectRatio >= 0.42 && aspectRatio <= 2.35) {
        if (aspectRatio >= 1) {
            width = 256;
            height = Math.floor(width / aspectRatio);
        } else {
            height = 256;
            width = Math.floor(height * aspectRatio);
        }
        this.setData({
            cWidth: width,   // 画布宽度(WXML)
            cHeight: height  // 画布高度(WXML)
        });
        // 开始绘图
        render(path, width, height);
    } else {
        // 提醒用户照片过长
    }
});

开始绘图

以下代码在函数compress中;
以下代码在长宽计算结束时被调用,声明应在wx.getImageInfo之前。

let render = (path, width, height) => {
    // 获取视图层的canvas节点,类似前端的 var xxx = document.getElementById("xxxx");
    wx.createSelectorQuery()
        .select('#compress') // 视图层canvas节点id
        .fields({
            node: true,
        })
        .exec(res => { // 回调,res是返回的多个节点组成的数组
            let canvas = res[0].node;
            canvas.width = width;
            canvas.height = height;
            let ctx = canvas.getContext('2d'); // canvas2d绘图上下文
            let img = canvas.createImage(); // 创建img对象(类似HTML的img标签)
            img.src = path;
            img.onload = () => {
                ctx.clearRect(0, 0, width, height); // 渲染前将画布清空(强迫症)
                // 新API的drawImage方法接收的第一个参数不再是url,而是img对象
                ctx.drawImage(img, 0, 0, width, height);
                wx.canvasToTempFilePath({
                        canvas,
                        x: 0,
                        y: 0,
                        destWidth: width,
                        destHeight: height,
                        fileType: 'jpg',
                        quality: 0.8
                    })
                    .then(res => {
                        cloudCheck(res.tempFilePath, path); // (重绘后的图片, 原图片)
                        // 若还有传入check的照片未压缩,重新调用compress(递归)
                        if (++i < checkList.length) {
                            compress(checkList, i);
                        };
                    });
            };
        });
}

读取buffer,调用云函数

imgSecCheckAPI提供HTTP请求和云调用两种方式,均以buffer的形式提交数据,且两种请求方式都无法在小程序端完成,因此我们需要读取缩略图的buffer,然后调用云函数。

let cloudCheck = (temp, origin) => {
    wx.getFileSystemManager().readFile({
        filePath: temp,
        success: buffer => {
            wx.cloud.callFunction({
                name: 'ContentCheck', //云函数的名称
                data: {
                    img: buffer.data
                },
            }).then(res => {
                console.log(res);
                if (res.result.errCode == 87014) {
                    // 发现敏感照片后所做的处理
                // 下面的imageR是云函数中定义的返回值
                } else if (res.result.imageR.errCode == 0) {
                    imageList.push(origin); // 将检查通过的照片添加至imageList,以便其他方法调用
                }
            })
        }
    });
};

云函数中的逻辑

关于云函数部署的详细过程,可参考小程序开发的内容安全审核

云函数中的逻辑不需太复杂,直接参考现有的其他案例就可以。不过为保证变量和案例中的命名一致,还是在此附上了云函数的代码:

exports.main = async (event, context) => {
    try {
        let imageR = false;

        //  检查图像内容是否违规
        if (event.img) {
            imageR = await cloud.openapi.security.imgSecCheck({
                media: {
                    header: {
                        'Content-Type': 'application/octet-stream'
                    },
                    contentType: 'image/jpg',
                    value: Buffer.from(event.img) // 官方文档这里是个坑
                }
            });
        };
        return {
            msgR,
            imageR
        };
    } catch (e) {
        return e
    }
};

相关文章

网友评论

      本文标题:通过security.imgSecCheck接口审核照片并实现多

      本文链接:https://www.haomeiwen.com/subject/ynxgrhtx.html