美文网首页
通过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