乐趣区

关于前端:小程序canvas实现图片压缩

原本用小程序写了一个本地化的图片利用,不存在服务端的交互行为

后果提交审核的时候还是被打回了

好的!马上整改

利用的交互大略就是这样

咱们须要在抉择图片后

对图片做一次平安校验

启用云开发

当初咱们须要一个 后端接口 来实现图片的 平安校验 性能

这时候长期搭个 Node 服务如同不太事实

又不是什么正经我的项目

于是就想到了微信的云开发性能

用起来实在方便快捷

至于图片的校验办法

间接用云函数调用 security.imgSecCheck 接口就好了

流程

chooseImage() {
  /// 用户抉择图片
  wx.chooseImage({
    count: 1,
    sizeType: ['original', 'compressed'],
    sourceType: ['album', 'camera'],
    success: async res => {if (res.errMsg === 'chooseImage:ok') {wx.showLoading({ title: '图片加载中'})
        // 获取图片长期地址
        const path = res.tempFilePaths[0]
        // 将图片地址实例化为图片
        const image = await loadImage(path, this.canvas)
        // 压缩图片
        const filePath = await compress.call(this, image, 'canvas_compress')
        // 校验图片合法性
        const imgValid = await checkImage(filePath)
        wx.hideLoading()
        if (!imgValid) return
        // 图片平安检测通过,执行后续操作
        ...
    }
  })
}

图片压缩

因为 security.imgSecCheck 对图片有尺寸限度

所以在图片上传前要先对超出尺寸的图片进行压缩解决

根本逻辑就是

超出尺寸的图片等比例放大就好了

咱们先要有一个 canvas 元素

用来解决须要压缩的图片

<template>
  <view class="menu-background">
    <view class="item replace" bindtap="chooseImage">
      <i class="iconfont icon-image"></i>
      <text class="title"> 图片 </text>
      <text class="sub-title"> 图片仅供本地应用 </text>
    </view>
    //
    // canvas
    //
    <canvas
      type="2d"
      id="canvas_compress"
      class="canvas-compress"
      style="width: {{canvasCompress.width}}px; height: {{canvasCompress.height}}px"
    />
  </view>
</template>

将 canvas 移到视线不可见到地位

.canvas-compress
    position absolute
    left 0
    top 1000px

图片进行压缩解决

/**
 * 压缩图片
 * 将尺寸超过标准的图片最小限度压缩
 * @param {Image} image 须要压缩的图片实例
 * @param {String} canvasId 用来解决压缩图片的 canvas 对应的 canvasId
 * @param {Object} config 压缩的图片标准 -> {maxWidth 最大宽度, maxHeight 最小宽度}
 * @return {Promise} promise 返回 压缩后的 图片门路
 */
export default function (image, canvasId, config = { maxWidth: 750, maxHeight: 1334}) {
  // 援用的组件传入的 this 作用域
  const _this = this
  return new Promise((resolve, reject) => {
    // 获取图片原始宽高
    let width = image.width
    let height = image.height
    // 宽度 > 最大限宽 -> 重置尺寸
    if (width > config.maxWidth) {
      const ratio = width / config.maxWidth
      width = config.maxWidth
      height = height / ratio
    }
    // 高度 > 最大限高度 -> 重置尺寸
    if (height > config.maxHeight) {
      const ratio = height / config.maxHeight
      height = config.maxHeight
      width = width / ratio
    }
    // 设置 canvas 的 css 宽高
    _this.canvasCompress.width = width
    _this.canvasCompress.height = height
    const query = this.createSelectorQuery()
    query
      .select(`#${canvasId}`)
      .fields({node: true, size: true})
      .exec(async res => {
        // 获取 canvas 实例
        const canvas = res[0].node
        // 获取 canvas 绘图上下文
        const ctx = canvas.getContext('2d')
        // 依据设施 dpr 解决尺寸
        const dpr = wx.getSystemInfoSync().pixelRatio
        canvas.width = width * dpr
        canvas.height = height * dpr
        ctx.scale(dpr, dpr)
        // 将图片绘制到 canvas
        ctx.drawImage(image, 0, 0, width, height)
        // 将 canvas 图片上传到微信临时文件
        wx.canvasToTempFilePath({
          canvas,
          x: 0,
          y: 0,
          destWidth: width,
          destHeight: height,
          complete (res) {if (res.errMsg === 'canvasToTempFilePath:ok') {
              // 返回临时文件门路
              resolve(res.tempFilePath)
            }
          },
          fail(err) {reject(err)
          }
        })
      })
  })
}

图片平安校验

云函数 checkImage.js
const cloud = require('wx-server-sdk')
cloud.init({env: cloud.DYNAMIC_CURRENT_ENV})
/**
 * 校验图片合法性
 * @param {*} event.fileID 微信云存储的图片 ID
 * @return {Number} 0: 校验失败;1: 校验通过
 */
exports.main = async (event, context) => {
  const contentType = 'image/png'
  const fileID = event.fileID
  try {
    // 依据 fileID 下载图片
    const file = await cloud.downloadFile({fileID})
    const value = file.fileContent
    // 调用 imgSecCheck 借口,校验不通过接口会抛错
    // 必要参数 media {contentType, value}
    const result = await cloud.openapi.security.imgSecCheck({
      media: {
        contentType,
        value
      }
    })
    return 1
  } catch (err) {return 0}
}
组件调用云函数封装
/**
 * 校验图片是否存在敏感信息
 * @param {String} filePath
 * @return {Promise} promise 返回校验后果
 */
export default function (filePath) {return new Promise((resolve, reject) => {
    // 先将图片上传到云开发存储
    wx.cloud.uploadFile({cloudPath: `${new Date().getTime()}.png`,
      filePath,
      success (res) {
        // 调用云函数 -checkImage
        wx.cloud.callFunction({
          name: 'checkImage',
          data: {fileID: res.fileID},
          success (res) {
            // res.result -> 0: 存在敏感信息;1: 校验通过
            resolve(res.result)
            if (!res.result) {
              wx.showToast({
                title: '图片可能含有敏感信息, 请从新抉择',
                icon: 'none'
              })
            }
          },
          fail (err) {reject(err)
          }
        })
      },
      fail (err) {reject(err)
      }
    })
  })
}
本文 demo

本文代码仓库

https://github.com/luosijie/f…

谢谢浏览!!!

退出移动版