小程序-云开发-图片内容安全检测解决思路

462 阅读2分钟

背景:

微信截图_20211014152023.png 如上图,我的小程序涉及图片功能上线还没两天就被人搞了(上传了黄图然后被举报)。通过查看得知UGC平台类小程序,涉及到平台内用户发布,平台都要对用户发布内容设置违法违规内容过滤机制。当天连夜修改于是有了这篇文章。

本文用到的图片内容安全检测为小程序自带的同步API:security.imgSecCheck

优点:免费(白嫖的),小程序自带,无须额外申请,格式支持PNG、JPEG、JPG、GIF,单个 appId 调用上限为 2000 次/分钟,200,000 次/天,基本满足需要。

缺点:图片大小限制1M ,图片尺寸不超过 750px x 1334px,还有就是这个API不太稳定的样子,超时的概率挺多,社区他们技术专员给的答案是服务高峰时有少量超时导致

虽然文档写的是图片尺寸不超过 750px x 1334px,但是实际用的时候大过了也基本不报错。

代码实现:

小程序端:
timer = null
state = {
    quality: 0.3,
    cWidth: 0,
    cHeight: 0,
}
//上传图片
chooseImage = () => {
        const _ = this;
        const { quality } = this.state
        Taro.chooseImage({
            count: 1,
            sizeType: ['original', 'compressed'],
            sourceType: ['album'],
            success(result) {
                const tempFilePath = result.tempFilePaths[0];
                let size = result.tempFiles[0].size;
                if (size > 1024 * 1024 * 8) {//大于8M返回
                    return Taro.showToast({
                        title: '图片大于8M啦',
                        icon: 'none'
                    })
                }
               if (size < 1024 * 800) { //小于800kb不压缩
                    const status = await _.imgSecCheck(tempFilePath)
                    if (status == true) {
                        console.log('通过了处理');
                        //走检测通过处理
                    }
                } else {
                    Taro.getImageInfo({
                        src: tempFilePath,
                        success: function (res) {
                            _.setState({//设置原始宽高
                                cWidth: res.width,
                                cHeight: res.height
                            }, async () => {
                                try {
                                    const imagePath = await _.getCanvasImg(tempFilePath, res.width, res.height, quality);
                                    const status = await _.imgSecCheck(imagePath)
                                    if (status == true) {
                                        console.log('通过了处理');
                                        //走检测通过处理
                                    }

                                } catch (error) {
                                }
                            })
                        }
                    })
                }
            }
        })
    }
 
    //压缩图片
    getCanvasImg(path, w, h, q) {
        return new Promise((resolve, reject) => {
            var _ = this;
            const ctx = Taro.createCanvasContext('myCanvas');
            ctx.clearRect(0, 0, w, h);
            ctx.drawImage(path, 0, 0, w, h);
            ctx.draw(false, function () {
                _.timer = setTimeout(() => {
                    Taro.canvasToTempFilePath({
                        canvasId: 'myCanvas',
                        fileType: 'jpg',
                        quality: q,
                        destWidth: w,
                        destHeight: h,
                        success: function success(res) {
                            _.setState({
                                imagePath:res.tempFilePath
                            })
                            clearTimeout(_.timer)
                            resolve(res.tempFilePath)
                        },
                        fail: function (e) {
                            clearTimeout(_.timer)
                            Taro.showModal({
                                title: '提示',
                                content: JSON.stringify(e),
                            })
                            reject(e)
                        }
                    });
                }, 500)
            });
        })
    }
    //检测图片
   async imgSecCheck(src) {
    try {
        //上传到云存储
        const uploadResult = await Taro.cloud.uploadFile({
        //云储存的路径及文件名
            cloudPath: new Date().getTime() + "-" + Math.floor(Math.random() * 1000),
            filePath: src,
        })
        //图片检测
        const json = await Taro.cloud.callFunction({
            name: "service",
            data: {
                action: "checkImg",
                fileID: uploadResult.fileID
            }
        })
        if (json.result.errCode == 0) { //图片没问题
            return true;
        }
        else if (json.result.errCode == 87014) {
            return Taro.showToast({
                title: '图片含有违法违规内容',
                duration: 2000,
                icon: 'none'
            });
        }
        else {
            return Taro.showToast({
                title: '请重新上传',
                duration: 2000,
                icon: 'none'
            });
        }
    } catch (error) {
        return Taro.showToast({
            title: error,
            duration: 2000,
            icon: 'none'
        });
    }
}
 render() {
        const { cWidth, cHeight} = this.state
        const _style = {
            width: cWidth + 'px',
            height: cHeight + 'px',
            position: 'fixed',
            top: '-9999px',
            left: '-9999px'
        }
      
        return (
            <View>
                <Button onClick={this.chooseImage}>上传</Button>
                <Canvas canvas-id='myCanvas' className='myCanvas' style={_style}></Canvas>
            </View>
        )
    }
云函数端:
module.exports = async (event, context) => {
    try {
        const fileID = event.fileID
        const res = await cloud.downloadFile({
            fileID: fileID,
        })
       //这里也可以把fileID和用户关联起来add在记录里,方便观察。
       
        const buffer = res.fileContent
        return await cloud.openapi.security.imgSecCheck({
            media: {
                contentType: "image/png",
                value: buffer
            }
        })

    } catch (err) {

        return err
    }
}

测了几波下来基本不报错,检测也正常。

啥,你问我为啥不用微信的wx.compressImage压缩图片接口api,因为它会反向压缩,压缩了还更大,社区里也有狠多反馈的问题。 以下是quality设置为0.3时几次压缩大小测试,可以看到压缩还可以。图片大时可以动态检测原始图片大小设置quality值。