由于公司业务拓展,急需基于uniapp生成支付宝小程序。之前已经成功将微信小程序和H5融合成一套码,故得知此需求的时候,笔者信心十足,但是本着实践出真知的想法,觉得还是得先调研一下uniapp在支付宝小程序的兼容性,并集成已有项目主体关键功能,为后续的技术调研方案做准备。在调研过程中,发现之前封装好的图片压缩方法在支付宝小程序上无法正常使用,重新阅读了官方文档后,又双更新了项目的图片压缩方法的使用流程。
之前封装好的压缩方案,原理是使用canvas现实的,但是在支付宝小程序端不生效,canvas相关的api存在但是不可用。
查阅文档后,给canvas添加了id区分支付宝小程序,可兼容之。
以下是官方文档原文
uni.createCanvasContext(canvasId, this)#定义
画布表示,传入定义在 <canvas/> 的 canvas-id或id(支付宝小程序是id、其他平台是canvas-id)
1,封装公用工具类compressImage.js
/** * 给的文件资源是否小于LimitSize (M), 小于走lessCallBack, 大于走moreCallBack */ export function imageSizeIsLessLimitSize(imagePath, limitSize, lessCallBack, moreCallBack) { uni.getFileInfo({ filePath: imagePath, success(res) { console.log('压缩前图片大小:', res.size / 1024, 'kb') if (res.size > 1024 * 1024 * limitSize) { moreCallBack() } else { lessCallBack() } } }) } // 主调用方法 /** * 获取小于限制大小的Image, limitSize默认为1M,递归调用。 */ export function getLessLimitSizeImage(canvasId, imagePath, limitSize = 1, drawWidth, callBack) { imageSizeIsLessLimitSize( imagePath, limitSize, (lessRes) => { callBack(imagePath) }, (moreRes) => { uni.getImageInfo({ src: imagePath, success: function(imageInfo) { var maxSide = Math.max(imageInfo.width, imageInfo.height) //画板的宽高默认是windowWidth var windowW = drawWidth var scale = 1 if (maxSide > windowW) { scale = windowW / maxSide } var imageW = Math.floor(imageInfo.width * scale) var imageH = Math.floor(imageInfo.height * scale) console.log('调用压缩', imageW, imageH) getCanvasImage(canvasId, imagePath, imageW, imageH, (pressImgPath) => { getLessLimitSizeImage(canvasId, pressImgPath, limitSize, drawWidth * 0.7, callBack) }) } }) } ) } /** * 获取画布图片 */ export function getCanvasImage(canvasId, imagePath, imageW, imageH, getImgsuccess) { const ctx = uni.createCanvasContext(canvasId) ctx.drawImage(imagePath, 0, 0, imageW, imageH) ctx.draw(false, () => { uni.canvasToTempFilePath({ canvasId: canvasId, x: 0, y: 0, width: imageW, height: imageH, quality: 1, success(res) { getImgsuccess(res.tempFilePath) } }) }) } export default { getLessLimitSizeImage, imageSizeIsLessLimitSize, getCanvasImage }
2,html调用并上传服务器:
<template> <view class="upload-page"> <view class="upload-tips">您最多可上传{{maxCount}}张图片</view> <view class="image-list"> <view v-for="(item, index) in fileList" :key="index" class="image-item"> <image class="image" mode="aspectFit" :src="item.path" @click="viewImage(index)"></image> <image class="image-delete" :src="imgUrl + 'doctor/img_delete.png'" @tap.stop="deleteImage" :data-index="index"></image> </view> <view v-if="fileList.length < maxCount" class="image-item" @tap="openAlbum"> <image class="image" :src="imgUrl + 'doctor/img_add.png'"></image> </view> </view> <view class="upload-btn" @tap="confirmUpload">确定</view> <canvas canvas-id="pressCanvas" id="pressCanvas" class="press-canvas"></canvas> </view> </template> <script> import { getLessLimitSizeImage } from './compressImage.js' export default { data() { return { imgUrl: process.env.VUE_APP_RESOURCE_URL, page: { json: {} }, type: '', fileList: [], maxCount: 9 } }, components: {}, props: {}, onLoad(options) { this.type = options.type this.page.json = options }, methods: { confirmUpload() { if (this.fileList.length === 0) { this.$util.showToast('请至少选择一张图片') return false } this.$Router.back() uni.$emit(this.page.json.emit, this.fileList) }, // 查看图片 viewImage(index) { let copyData = [], data = [].concat(this.fileList) data.forEach((v) => { copyData.push(v.path) }) uni.previewImage({ current: copyData[index], urls: copyData }) }, // 删除图片 deleteImage(e) { let { index } = e.currentTarget.dataset this.fileList.splice(index, 1) }, // 打开相册 openAlbum() { let length = this.maxCount - this.fileList.length uni.chooseImage({ count: length, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success: (res) => { this.upLoadImgs(res.tempFilePaths, this.type) } }) }, // 上传多张图片 upLoadImgs(files, type) { uni.showLoading() let promises = files.map((item) => { return this.uploadImg(item, type) }) Promise.all(promises) .then((datas) => { // 所有上传完成后 this.fileList = datas.length > 0 && this.fileList.concat(datas) uni.hideLoading() }) .catch(() => { uni.hideLoading() }) }, // 上传图片 uploadImg(file, type) { return new Promise((resolve, reject) => { getLessLimitSizeImage('pressCanvas', file, 1, 750, (imagePath) => { /* #ifdef H5 */ let devicetype = 'h5' /* #endif */ /* #ifdef MP-WEIXIN */ let devicetype = 'applet' /* #endif */ /* #ifdef MP-ALIPAY */ let devicetype = 'alipay' /* #endif */ uni.uploadFile({ url: process.env.VUE_APP_API_URL + 'client/v1/file/images', header: { 'access-token': this.$store.state.user.accessToken, version: process.env.VUE_APP_VERSION, 'version-code': process.env.VUE_APP_VERSION_CODE, devicetype: devicetype }, fileType: 'image', filePath: imagePath, name: 'file', formData: { source: 'inquiryApply', type: '' }, success: (res) => { let image = JSON.parse(res.data) console.log('uploadFile success:', image) if (image.code === 200) { resolve(image.data[0]) } else { this.$util.showModal(image.msg || '图片上传失败') reject(image) } }, fail: (err) => { console.log('uploadFile fail:', JSON.stringify(err || {})) if (err.hasOwnProperty('errMsg') && err.errMsg.indexOf('timeout') > 0) { this.$util.showModal('上传超时,请稍后再试') } else { this.$util.showModal('图片上传失败,请稍后再试') } reject(err) }, complete: () => {} }) }) }) } } } </script> <style lang="scss" scoped> .upload-page { position: relative; width: 750rpx; height: 100vh; background-color: #ffffff; } .upload-tips { width: 750rpx; height: 80rpx; background-color: #fffbe8; font-size: 28rpx; color: #ed6a0c; line-height: 80rpx; text-align: center; } .image-list { box-sizing: border-box; display: flex; flex-wrap: wrap; justify-content: flex-start; align-items: center; width: 750rpx; padding: 30rpx; } .image-item { position: relative; box-sizing: border-box; width: 210rpx; height: 210rpx; margin-right: 30rpx; margin-bottom: 30rpx; border: 1rpx solid #ebedf0; } .image-item:nth-child(3n) { margin-right: 0; } .image-item .image { width: 210rpx; height: 210rpx; } .image-item .image-delete { position: absolute; top: -30rpx; right: -30rpx; width: 40rpx; height: 40rpx; padding: 10rpx; } .upload-btn { position: fixed; bottom: 0; left: 0; width: 750rpx; height: 100rpx; background-color: $primary; font-size: 30rpx; color: #ffffff; line-height: 100rpx; text-align: center; } .press-canvas { position: absolute; top: -1000px; left: -1000px; background-color: gray; width: 750px; height: 750px; } </style>
原理上是使用canvas实现的,小于100k的图片不做压缩。
var {getCanvasImg} = require('../../common/img-compression.js'); // 在page上面按需引入
然后在wx.chooseImage接口成功回调中调用(使用tempFiles字段,因为它包含了图片大小)
let tempFiles = result.tempFiles; let i = 0; var arr = []; // par1:this; par2:图片压缩数组,含size字段;par3:递增值,默认先传入0; par4:接收压缩结果数组; par5:设置画布大小回调函数; par6:设置压缩后的图片数组 getCanvasImg(that, tempFiles, i, that.data.quality, arr, that.setCanvasSize, that.setCompImg);
在定义两个回调函数,用于设置画布大小和接收压缩结果
// 设置画布大小 setCanvasSize(cWidth, cHeight){ this.setData({ cWidth, cHeight }); }, // 接收压缩结果 setCompImg(imagePath){ this.setData({ imagePath // 压缩结果 }) },
至此使用步骤已介绍完毕
以上就是笔者分享的图片压缩上传的方法封装及使用啦,完美兼容支付宝小程序,微信小程序及H5三端。