背景
uni-app框架开发微信小程序
项目中有一个小程序上传图片的需求,这里遇到一个问题:手机拍照的图片过大,多张图片上传时图片总体积过大,调用后台接口进行图片上传有一定的带宽压力,频繁出现请求超时的问题,此时我们想了两种方案:
1、前端直接调用七牛云的SDK进行图片上传(我们的静态文件存储在7牛云上),接入成本过高
2、在前端先将图片做压缩处理,然后再通过接口上传,经过demo测试,这种方式虽然是有损压缩,但是压缩比例非常可观
最终我们采用了第二种方案进行图片压缩处理,在前端图片处理中,单张图片的处理时间大概是数秒级,多张图片压缩的时间大概在10秒级别,时间上可以接受,处理步骤如下:
uni.compressImage图片压缩接口
uni-app官网提供的压缩图片接口:uni.compressImage(OBJECT),该接口在开发工具和ios系统表现良好,但在安卓机上,没有效果,不支持H5,而且压缩比在手机跟开发工具不一致,我们决定通过canvas绘制压缩图片。
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