小程序图片压缩

799 阅读3分钟

背景

uni-app框架开发微信小程序

项目中有一个小程序上传图片的需求,这里遇到一个问题:手机拍照的图片过大,多张图片上传时图片总体积过大,调用后台接口进行图片上传有一定的带宽压力,频繁出现请求超时的问题,此时我们想了两种方案:

1、前端直接调用七牛云的SDK进行图片上传(我们的静态文件存储在7牛云上),接入成本过高

2、在前端先将图片做压缩处理,然后再通过接口上传,经过demo测试,这种方式虽然是有损压缩,但是压缩比例非常可观

最终我们采用了第二种方案进行图片压缩处理,在前端图片处理中,单张图片的处理时间大概是数秒级,多张图片压缩的时间大概在10秒级别,时间上可以接受,处理步骤如下:

uni.compressImage图片压缩接口

uni-app官网提供的压缩图片接口:uni.compressImage(OBJECT),该接口在开发工具和ios系统表现良好,但在安卓机上,没有效果,不支持H5,而且压缩比在手机跟开发工具不一致,我们决定通过canvas绘制压缩图片。

image.png

canvas绘制压缩图片

用到的uni-app API接口有:

1、选择图片:uni.chooseImage(OBJECT)

uni.chooseImage({
    count: 1, //默认9
    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
    sourceType: ['album','camera'], //从相册选择 或 拍照
    success: (res)=> {
        console.log(JSON.stringify(res.tempFilePaths));
    }
})

2、获取图片信息: uni.getImageInfo(OBJECT)

uni.getImageInfo({
    src: res.tempFilePaths[0], // res.tempFilePaths[0]:chooseImage接口返回。
    success: (res)=> {
        console.log(res.width);
        console.log(res.height);
    }
});

3、创建 canvas: uni.createCanvasContext(canvasId)

2.3.1 创建一个canvas标签

<canvas canvas-id="myCanvas" :style="{width: w + 'px', height: h + 'px'}"></canvas>

//页面上不需要显示canvas,通过css定位将它移开:    
canvas{
    position: absolute;
    left: -99999px;
    top:-99999px;
}

2.3.2  通过接口创建canvas, 压缩图片分辨率后再绘制图片

// 图片分辨率压缩 
const calcImageSize = (res, pixels) => {
    let imgW = res.width, 
        imgH = res.height,
        ratio;
    if((ratio = imgW * imgH / pixels) > 1) {
        ratio = Math.sqrt(ratio)
        imgW = parseInt(imgW / ratio)
        imgH = parseInt(imgH / ratio)
    }
    return { imgW, imgH }
}

let canvas = uni.createCanvasContext("myCanvas")
// 图片分辨率压缩
let { w, h } = calcImageSize(res, pixels); // res: getImageInfo接口返回;pixels图片分辨率:图片宽*高,可以按照需求灵活设置,可设置1000000,那就是图片宽高会按照比例调整为100 * 100 以内
canvas.drawImage(res.path, 0, 0, w, h)
canvas.draw()

4、 canvas 根据配置生成指定大小的图片,并返回文件路径:uni.canvasToTempFilePath(object, component)

//draw完毕后延迟调用canvasToTempFilePath, w、h: calcImageSize方法return的结果
setTimeout(() => {
    uni.canvasToTempFilePath({
      x: 0,
      y: 0,
      width: w, //画布宽度(默认为canvas宽度-x)
      height: h, //画布高度(默认为canvas高度-y)
      destWidth: w, //输出图片宽度(默认为 width * 屏幕像素密度)
      destHeight: h, //输出图片高度(默认为 height * 屏幕像素密度)
      quality:1, //图片的质量,取值范围为 (0~1),不在范围内时当作1.0处理
      fileType:"png",  //目标文件的类型,只支持 'jpg' 或 'png'。默认为 'png'
      canvasId: "myCanvas", //画布标识,传入 `<canvas/>` 的 canvas-id(支付宝小程序是id、其他平台是canvas-id)
      success: (res)=> {
        // 在H5平台下,tempFilePath 为 base64, 返回压缩后的图片路径
        console.log(res.tempFilePath)
      } 
    })
},500)

完整代码

<template>
    <!-- 标签 -->
    <view class="compress_canvas">
         <canvas canvas-id="myCanvas" :style="{width: w + 'px', height: h + 'px'}"></canvas>
     </view>
</template>
<script setup>
// canvas 的宽高 生成图片设置的质量
 let w = 0,h = 0,quality=0.8;
 // 图片分辨率压缩 
const calcImageSize = (res, pixels) => {
    let imgW = res.width, 
        imgH = res.height,
        ratio;
    if((ratio = imgW * imgH / pixels) > 1) {
        ratio = Math.sqrt(ratio)
        imgW = parseInt(imgW / ratio)
        imgH = parseInt(imgH / ratio)
    }
    return [ imgW, imgH ]
}
// 图片压缩
const compressImageSize = ()=>{
    //选择图片
    uni.chooseImage({
        count: 1, 
        sizeType: ['original', 'compressed'], 
        sourceType: ['album','camera'], 
        success: (imgRes)=> {
            //获取图片信息
            uni.getImageInfo({
                src: imgRes.tempFilePaths[0],
                success: (infoRes)=> {
                    const pixels = 500000;
                    let canvas = uni.createCanvasContext("myCanvas");
                    [ w, h ] = calcImageSize(infoRes, pixels);
                    canvas.drawImage(infoRes.path, 0, 0, w, h)
                    canvas.draw()
                    setTimeout(() => {
                        uni.canvasToTempFilePath({
                          x: 0,
                          y: 0,
                          width: w, 
                          height: h, 
                          destWidth: w, 
                          destHeight: h,
                          quality:quality,
                          fileType:"png",
                          canvasId: "myCanvas",
                          success: (pathRes)=> {
                            // 返回压缩后的图片路径
                            console.log(pathRes.tempFilePath)
                          } 
                        })
                    },500)
                }
            })
        }
    })
}
</script>
<style>
.compress_canvas{
    position: absolute;
    left: -99999px;
    top:-99999px;
}
</style>

测试数据

(分辨率处理,质量(quality)影响最终的图片大小):

测试数据设置的分辨率:500000, quality:0.8

image.png

image.png