preface

Image security detection is slightly more complex than text security detection, and when you finish this article, you will get to

  • Application scenarios of image security detection
  • A safe way to solve the picture
  • Image detection using cloud invocation in cloud development
  • How to limit the size of uploaded pictures
  • How to solve the problem of multi-graph upload coverage

If you gain something, don’t forget to triple attack (like, comment, share ~)

Take a look at the finished example effect

When users upload sensitive and illegal pictures, users are forbidden to upload and publish, and make corresponding user-friendly prompts

Complete the UI layout

For WXML and WXSS, you can modify, this article focuses on the image security verification

<view class="image-list"> <! --> <block wx:for="{{images}}" wx:key="*this">
      <view class="image-wrap">
        <image class="image" src="{{item}}" mode="aspectFill" bind:tap="onPreviewImage" data-imgsrc="{{item}}"></image>
        <i class="iconfont icon-shanchu" bind:tap="onDelImage" data-index="{{index}}"></i> </view> </block> <! Select image --> <view class="image-wrap selectphoto" hidden="{{! selectPhoto}}" bind:tap="onChooseImage">
      <i class="iconfont icon-add"></i>
    </view>
  </view>

<view class="footer">
    <button class="send-btn"  bind:tap="send"</button> </view>Copy the code

Corresponding WXSS code

.footer {
  display: flex;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
  background: #34bfa3;
}

.send-btn {
  width: 100%;
  color: #fff;
  font-size: 32rpx;
  background: #34bfa3;} button { border-radius: 0rpx; } button::after { border-radius: 0rpx ! important; } /* image-style */. Image-list {display: flex; flex-wrap: wrap; margin-top: 20rpx; } .image-wrap { width: 220rpx; height: 220rpx; margin-right: 10rpx; margin-bottom: 10rpx; position: relative; overflow: hidden; text-align: center; } .image { width: 100%; height: 100%; } .icon-shanchu { position: absolute; top: 0; right: 0; width: 40rpx; height: 40rpx; background-color:# 000;Opacity: 0.4; color:#fff;
  text-align: center;
  line-height: 40rpx;
  font-size: 38rpx;
  font-weight: bolder;
}

.selectphoto {
  border: 2rpx dashed #cbd1d7;
  position: relative;
}

.icon-add {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: #cbd1d7;
  font-size: 60rpx;
}
Copy the code

The final rendered UI looks like this

/* * The API involved :wx.chooseImage selects images from the local album or takes photos with the camera * * * * (https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.chooseImage.html) / / / maximum number of upload pictures const MAX_IMG_NUM = 9; const db = wx.cloud.database(); // Initialize the cloud database Page({/** * the initial data of the Page */ data: {images: [], // store the uploaded image in an array object selectPhoto:true// Add +icon element to display}, /** * lifecycle function -- listen to page load */ onLoad:function(options) {}, // Select the imageonChooseImage() {// You can also select a few more pictures. The initial value is set to the maximum number of pictures - the length of the current pictureletmax = MAX_IMG_NUM - this.data.images.length; Wx. chooseImage({count: Max, // count indicates the maximum number of images to select.'original'.'compressed'], // The size of the selected imagesourceType: ['album'.'camera'], // Select the source of the image success: Console. log(res) this.setData({// tempFilePath can be used as the SRC attribute of the IMG tag to display images images: This.data.images.concat (res.tempFilepaths)}) Max = MAX_IMG_NUM - this.data.images.length this.setData({this.data.images.length}) selectPhoto: max <= 0 ?false : true/ / when more than 9 zhang, plus hidden})},}}), / / click the delete icon at the upper right, delete pictures onDelImage operation (event) {const index. = the event target. The dataset. The index; Splice (index, 1) this.setData({images: {this.data.images.splice(index, 1) this.setData({images: This.data.images}) // When the maximum number of images is added, the Add button is hidden to prevent new images from being addedif (this.data.images.length == MAX_IMG_NUM - 1) {
      this.setData({
        selectPhoto: true,})}},})Copy the code

The final front-end UI effect is as follows:

Application scenarios

In general, verifying whether an image contains illegal content is just as important as verifying text security, and there are several applications

  • Intelligent yellow detection of pictures: tool applications related to photography (such as beauty photography, image recognition applications) user photo upload detection; Picture detection of e-commerce products on shelves; Media user articles in the picture detection
  • Sensitive face recognition: user profile picture; Media user articles in the picture detection; Images uploaded by social users should be detected, and any content spontaneously produced by users should be detected in advance

Security means to solve the picture

In applets development, there are two approaches

  • HTTPS call
  • Cloud call HTTPS call request interface stop
https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN
Copy the code

Check image audit, according to the official document, need two mandatory parameters: respectively :access_token(interface call certificate),media(image file to be tested) For HTTPS call methods, you can refer to text content security detection (part 1) processing methods, processing is similar, this paper mainly focuses on cloud call cloud development

Cloud invocation

Create the cloudfunction imgSecCheck under the CloudFunctions directory folder

config.json

{
  "permissions": {
    "openapi": [
      "security.imgSecCheck"]}}Copy the code

After the configuration is complete, in the main entry index.js, pass the media object through the security.imgSecCheck interface, as shown below

// cloud function entry file const cloud = require('wx-server-sdk'); cloud.init({ env: Cloud.dynamic_current_env}) // cloud function entry function exports.main = async (event, context) => { const wxContext = cloud.getWXContext() try { const result = await cloud.openapi.security.imgSecCheck({ media: { contentType:'image/png', value: buffer. from(event.img) // The Buffer must be converted from the small program side, otherwise the error will be reported, interface exception}})if (result && result.errCode.toString() === '87014') {
      return { code: 500, msg: 'Content contains illegal content', data: result }
    } else {
      return { code: 200, msg: Content of the 'ok', data: result}}} catch (err) {// Error handlingif (err.errCode.toString() === '87014') {
      return { code: 500, msg: 'Content contains illegal content', data: err }
    }
    return { code: 502, msg: ImgSecCheck interface failed to be called, data: err }
  }
}
Copy the code

What you’ll notice is that on the cloud side, in just a few lines of code, you’ve done the image security check and on the applet side, the code looks like this

/ / miniprogram/pages/imgSecCheck/imgSecCheck js / / maximum number of upload pictures const MAX_IMG_NUM = 9; Const db = wx.cloud.database() Page({/** ** the initial data of the Page */ data: {images: [], selectPhoto:true}, /** * lifecycle function -- listen to page load */ onLoad:function(options) {}, // Select the imageonChooseImage() { // const that = this; // If the arrow function is used, then this line of code is not needed, just use this. // You can also select several images, the initial value is set to the maximum number of images - the current image lengthlet max = MAX_IMG_NUM - this.data.images.length; 
    wx.chooseImage({
      count: max,
      sizeType: ['original'.'compressed'].sourceType: ['album'.'camera'], success: Console. log(res) => {// If this is not the arrow function, this should be the temporary variable of that Const tempFiles = res.tempFiles; const tempFilePath; this.setData({ images: this.data.images.concat(res.tempFilePaths) }) // When choosing pictures, pictures of local temporary storage, this time, for the calibration of the picture, of course you in the last click publish, also can check, is merely a front the problem of calibration and rear, personally, I tend to choose the image calibration, choose a few photos, you should do the safety judgment when the choice stage, Console. log(tempFiles); tempFiles.forEach(items => { console.log(items); Wx.getfilesystemmanager ().readfile ({filePath: items.path, success: res => {console.log(res); wx.cloud.callFunction({ name:'imgSecCheck',
                    data: {
                      img: res.data
                    }
            })
            .then(res => {
               console.log(res);
               let { errCode } = res.result.data;
               switch(errCode) {
                 case 87014:
                   this.setData({
                      resultText: 'Content contains illegal content'
                   })
                   break;
                 case 0:
                   this.setData({
                     resultText: Content of the 'OK'
                   })
                   break;
                 default:
                   break; } }) .catch(err => { console.error(err); }) }, fail: err => { console.error(err); }})} Max = MAX_IMG_NUM - this.data.images.length this.setData({selectPhoto: Max <= 0?false : true/ / when more than 9 zhang, plus hidden})},}}), / / delete pictures onDelImage (event) {const index. = the event target. The dataset. The index; // Delete the current image, use the splice method, delete one, remove one from the array this.data.images.splice(index, 1); This.setdata ({images: this.data.images}) // When the maximum number of images is added, the Add button is hidden to prevent new images from being addedif (this.data.images.length == MAX_IMG_NUM - 1) {
      this.setData({
        selectPhoto: true,})}},})Copy the code

The following is an example effect:

How to limit the size of uploaded images

Wx. ChooseImage = wx. ChooseImage = wx. ChooseImage = wx. ChooseImage = wx. ChooseImage = wx

The specific example code is shown below

// Select the imageonChooseImage() { const that = this; // You can also select several more images. The initial value is set to the maximum number of images - the current image lengthlet max = MAX_IMG_NUM - this.data.images.length; 
    wx.chooseImage({
      count: max,
      sizeType: ['original'.'compressed'].sourceType: ['album'.'camera'], success: (res) => { console.log(res) const tempFiles = res.tempFiles; this.setData({ images: This.data.images.concat (res.tempFilepaths) // tempFilePath can be used as the SRC property of the IMG tag to display images}) // When choosing pictures, pictures of local temporary storage, this time, for the calibration of the picture, of course you in the last click publish, also can check, is merely a front the problem of calibration and rear, personally, I tend to choose the image calibration, choose a few photos, you should do the safety judgment when the choice stage, Console. log(tempFiles); tempFiles.forEach(items => {if(items && items. Size > 1 * (1024 * 1024)) {// Limit the size of images wx.showtoast ({icon:'none',
              title: 'Uploading more than 1M images is prohibited', duration: 4000 }) } console.log(items); Wx.getfilesystemmanager ().readfile ({filePath: items.path, success: res => {console.log(res); Wx.cloud. callFunction({// request to call imgSecCheck name:'imgSecCheck',
                    data: {
                      img: res.data
                    }
            })
            .then(res => {
               console.log(res);
               let { errCode } = res.result.data;
               switch(errCode) {
                 case 87014:
                   this.setData({
                      resultText: 'Content contains illegal content'
                   })
                   break;
                 case 0:
                   this.setData({
                     resultText: Content of the 'OK'
                   })
                   break;
                 default:
                   break; } }) .catch(err => { console.error(err); }) }, fail: err => { console.error(err); }})} Max = MAX_IMG_NUM - this.data.images.length this.setData({selectPhoto: Max <= 0?false : true})},})},Copy the code

Note:

If you feel that the image security interface provided by wechat official can not meet your business needs, you can choose some other image content security verification interface

This image security verification is very necessary. Once users upload illegal images and spread them through the network, the platform is responsible for the social impact

How to solve the problem of uploading and overwriting multiple images

For the detected pictures, we often need to store it in the cloud database (of course, you can upload the user’s unique identifier OpenID, nickname, avatar, time and so on to the cloud database), so that it can be used in other places, so how to achieve this in the small program cloud development?

We need to store the locally selected image fileID in the cloud database collection

In the applet, the API used to upload images to cloud storage is wx.cloud.uploadFile, which uploads local resources to cloud storage space

Note: Uploads to the same path will be overwritten

For uploading images, the Wx.cloud. uploadFileAPI can only upload one image, but in many cases, it is necessary to upload multiple images to the cloud storage. When clicking publish, we want to upload multiple images to the cloud storage

The API can only upload one image at a time, but you can loop through multiple images and upload them one by one

Note: The name of the file

How to ensure that the uploaded picture is not overwritten, the file is not the same as the case will not be overwritten

When selecting images, you should not upload them, because users may delete them and other operations. If you upload them directly, it will cause a waste of resources

Instead, upload should be performed when the publish button is clicked, as shown in the code below

      let promiseArr = []
      letFileIds = [] // Store the fileIds of images in an arrayletimgLength = this.data.images.length; // Image uploadfor (let i = 0; i < imgLength; i++) {
        let p = new Promise((resolve, reject) => {
        letItem = this.data.images[I] // File extensionletsuffix = /\.\w+$/.exec(item)[0]; UploadFile ({// Use cloudPath to upload files:'blog/' + Date.now() + The '-'+ math.random () * 1000000 + suffix, // Cloud storage path filePath: item, // Path to upload file resources SUCCESS: (res) => {console.log(res); Console. log(res.fileid) fileIds = fileids. concat(res.fileid) resolve()}, fail: (err) => { console.error(err) reject() } }) }) promiseArr.push(p) } // Promise.all(), wait for all tasks to be performed, wait for all images to be uploaded, and then store the data in the database promise.all (promiseArr).then((res) => { db.collection('blog'Data: {img: fileIds, createTime: {img: fileIds, createTime: Db. ServerDate (), / / server time}}). Then ((res) = > {the console. The log (res); this._hideToastTip(); this._successTip(); })}). The catch ((err) = > {/ / release failure console. Error (err); })Copy the code

Above, a distinction is made by using the current time + random number to avoid the problem of uploading files with the same name

Because of this upload interface, only one image can be uploaded at a time, so it is necessary to loop through the image, and then upload it one by one, one is uploaded to the cloud storage

The other is to add to the cloud database collection, pay attention to the two operations respectively, the cloud database image is taken from the cloud storage, and then added to the cloud database to the example effect as follows:

Note: you need to manually create the collection, otherwise you will not be able to upload to the cloud database, will report an error, the data collection in the example is blog

At this point, the detection of sensitive pictures, as well as the uploading of multiple pictures here has been completed as follows is a complete small program side logic example code

/ / miniprogram/pages/imgSecCheck/imgSecCheck js / / maximum number of upload pictures const MAX_IMG_NUM = 9; Const db = wx.cloud.database() Page({/** ** the initial data of the Page */ data: {images: [], selectPhoto:true}, /** * lifecycle function -- listen to page load */ onLoad:function(options) {}, // Select the imageonChooseImage() { const that = this; // You can also select several more images. The initial value is set to the maximum number of images - the current image lengthlet max = MAX_IMG_NUM - this.data.images.length;
    wx.chooseImage({
      count: max,
      sizeType: ['original'.'compressed'].sourceType: ['album'.'camera'], success: (res) => { console.log(res) const tempFiles = res.tempFiles; this.setData({ images: This.data.images.concat (res.tempFilepaths) // tempFilePath can be used as the SRC property of the IMG tag to display images}) // When choosing pictures, pictures of local temporary storage, this time, for the calibration of the picture, of course you in the last click publish, also can check, is merely a front the problem of calibration and rear, personally, I tend to choose the image calibration, choose a few photos, you should do the safety judgment when the choice stage, Console. log(tempFiles); tempFiles.forEach(items => {if (items && items.size > 1 * (1024 * 1024)) {
            wx.showToast({
              icon: 'none',
              title: 'Uploading more than 1M images is prohibited', duration: 4000}) // Prohibit uploading pictures over 1M} console.log(items); Wx.getfilesystemmanager ().readfile ({filePath: items.path, success: res => {console.log(res); this._checkImgSafe(res.data); // Check the image security check}, fail: err => {console.error(err); }})} Max = MAX_IMG_NUM - this.data.images.length this.setData({selectPhoto: Max <= 0?false : true/ / when more than 9 zhang, plus hidden})},}}), / / delete pictures onDelImage (event) {const index. = the event target. The dataset. The index; // Delete the current image, use the splice method, delete one, remove one from the array this.data.images.splice(index, 1); This.setdata ({images: this.data.images}) // When the maximum number of images is added, the Add button is hidden to prevent new images from being addedif (this.data.images.length == MAX_IMG_NUM - 1) {
      this.setData({
        selectPhoto: true,})}}, // Click the publish button to upload the picture to the cloud databasesend() {
    const images = this.data.images.length;
    if (images) {
      this._showToastTip();
      let promiseArr = []
      let fileIds = []
      letimgLength = this.data.images.length; // Image uploadfor (let i = 0; i < imgLength; i++) {
        let p = new Promise((resolve, reject) => {
          letItem = this.data.images[I] // File extensionletsuffix = /\.\w+$/.exec(item)[0]; Wx.cloud. uploadFile({cloudPath:'blog/' + Date.now() + The '-'+ Math.random() * 1000000 + suffix, filePath: item, success: (res) => { console.log(res); console.log(res.fileID) fileIds = fileIds.concat(res.fileID) resolve() }, fail: (err) => {console.error(err) reject()}})}) promise.all (promise.reject).then((res) => {console.error(err) reject()}})}) promise.push (p) db.collection('blog'Data: {img: fileIds, createTime: {img: fileIds, createTime: Db. ServerDate (), / / server time}}). Then ((res) = > {the console. The log (res); this._hideToastTip(); this._successTip(); })}). The catch ((err) = > {/ / release failure console. Error (err); })}else {
      wx.showToast({
        icon: 'none',
        title: 'No image selected, can't publish'_checkImgSafe(data) {wx.cloud.callFunction({name:'imgSecCheck',
        data: {
          img: data
        }
      })
      .then(res => {
        console.log(res);
        let {
          errCode
        } = res.result.data;
        switch (errCode) {
          case 87014:
            this.setData({
              resultText: 'Content contains illegal content'
            })
            break;
          case 0:
            this.setData({
              resultText: Content of the 'OK'
            })
            break;
          default:
            break; } }) .catch(err => { console.error(err); })},_showToastTip() {
    wx.showToast({
      icon: 'none',
      title: 'In release... ',}}),_hideToastTip() {
    wx.hideLoading();
  },

  _successTip() {
    wx.showToast({
      icon: 'none',
      title: 'Released successfully',})}})Copy the code

The complete example, WXML, is shown below

<view class="image-list"> <! --> <block wx:for="{{images}}" wx:key="*this">
      <view class="image-wrap">
        <image class="image" src="{{item}}" mode="aspectFill" bind:tap="onPreviewImage" data-imgsrc="{{item}}"></image>
        <i class="iconfont icon-shanchu" bind:tap="onDelImage" data-index="{{index}}"></i> </view> </block> <! Select image --> <view class="image-wrap selectphoto" hidden="{{! selectPhoto}}" bind:tap="onChooseImage">
      <i class="iconfont icon-add"></i>
    </view>
  </view>

<view class="footer">
    <button class="send-btn"  bind:tap="send"</button> </view> <view>Copy the code

You can according to their own business logic needs, once detected picture violation, disable button state, or give some users prompt, it is ok, before publishing or click publish, image content security check can be, once found a picture violation, do not allow to continue the following operation

conclusion

Security. ImgSecCheck

Interface, the realization of the image security verification, implementation, is quite convenient, for basic verification, using the interface provided by the official, has been enough, but if you want to more strict detection, you can introduce some third-party content security verification, to ensure that the content is more secure

How to limit the size of uploaded pictures, and solve the problem of uploading and covering the same picture

If you still have questions about the security detection of pictures or text content, please leave a comment below and discuss with us

For more information, you can follow the wechat itclanCoder public account, a useful account that only delivers and shares enlightening wisdom to you