【开发】微信小程序图片上传压缩代码及注意事项
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
上传图片是小程序常见的功能,例如点评类小程序邀请用户分享照片、电商类小程序要求商家上传商品照片。 伴随着照片像素越来越高,图片体积越来越大,小程序开发者需要压缩图片,否则将导致用户上传图片失败或加载时间过长等影响体验的情况。 小程序提供 wx.chooseMedia、wx.canvasToTempFilePath、wx.compressImage 3 个图片类接口,便于开发者在不同应用场景下处理图片。除此以外,这 3 个接口的巧妙结合能够满足更多元化的图片压缩需求。下面就来看看怎样使用吧! wx.chooseMedia wx.chooseMedia 支持在使用小程序过程中拍摄或从手机相册选择图片或视频,其 sizeType 属性支持是否上传缩略图。该接口应用简便,接入即可实现压缩图片效果,省时省力。 wx.chooseMedia({ count: 9, mediaType: ['image'], // 只允许选择图片 sourceType: ['album', 'camera'], // 可以拍摄或从相册中选择 sizeType:['compressed'], // 选择压缩图 camera: 'back', // 后置摄像头 success(res) { console.log(res) } }); 然而,该接口在压缩图片方面也有一定的限制: 无法指定压缩质量 部分安卓机型存在压缩失效的情况 iOS 和安卓的压缩机制不同,需要进行合理兼容 wx.canvasToTempFilePath 开发者可以通过控制 Canvas.createImage 绘制图片到 Canvas,然后利用 wx.canvasToTempFilePath 接口转换成图片。 这种方法能够高效控制图片宽高尺寸以及压缩质量,非常适用于有图片要求的场景。 wx.canvasToTempFilePath({ width: 50, // 画布区域的宽度 height: 50, // 画布区域的高度 destWidth: 100, // 输出图片的宽度 destHeight: 100, // 输出图片的高度 canvasId: 'myCanvas', quality: 1, // 图片质量0-1 success(res) { console.log(res.tempFilePath) } }); 但是这种方式也会存在一定的限制: iOS 和安卓的压缩机制不同,需要进行合理兼容 通过 Canvas 转换的图片存在略微色差 wx.compressImage 开发者可以调用wx.compressImage 接口直接压缩图片,而且支持选择压缩质量,不限制图片宽高尺寸,非常适用于处理特殊大小的图片。 wx.compressImage({ src: '', // 图片路径 quality: 80 // 压缩质量 0-100 }); 同时这种方式也需要考虑不同系统的压缩差异: 在压缩到极限值时,iOS 压缩图画质不会随着压缩质量变小而变化 在压缩质量小于 1 时,安卓系统输出的画质将不再变小 多方式结合处理 回顾常见的小程序业务场景,图片处理主要聚焦于用户上传图片、列表展示这 2 个环节,可以结合以上 3 个接口实现最佳图片处理方式,既能够利用接口自带的压缩功能,省时省力;又能够解决图片太大造成的压缩难题。 判断系统类型 判断当前系统是 iOS 系统还是安卓系统 function isIOS(){ return wx.getSystemInfo().then(res => { return /IOS/ig.test(res.system); }); 根据系统选择上传方式 iOS 系统:设置 sizeType 为 [‘compressed’],利用 iOS 压缩体系自动压缩 安卓系统:设置 sizeType 为 [‘original’, ‘compressed’],让用户自主选择上传原图或压缩图。 这种方式一方面利用接口自带的压缩能力; 另一方面如果图片宽高大于安卓能清晰压缩的值(例如40000),用户会预览到比较模糊的照片而选择上传原图 验证大小,手动压缩 当用户选择图片后,wx.chooseMedia 返回的 tempFiles 显示对应图片的大小。如果该图片大小大于限制值,则进行手动压缩。 根据宽高选择压缩方式 通过 wx.getImageInfo 获取图片的宽高: 如果宽度或高度大于 4096,调用 wx.compressImage 强制压缩 如果宽度和高度都小于 4096,绘制 Canvas 实现压缩,设置压缩基础宽高为 1280 代码如下: // compressImage.js /** * @param {object} img 包含path:图片的path,size:图片的大小 * @param {object} canvas canvas对象 * @param {number} fileLimit 文件大小限制 * @returns {Promise} 返回Promise对象 */ function _compressImage(img, canvas, fileLimit) { return wx.getSystemInfo().then(res => { let { // 设备像素比 pixelRatio, // 设备品牌 system } = res; // 是否是IOS系统 let isIOS = /(ios)/ig.test(system); // 文件限制 fileLimit = fileLimit || 2 * 1024 * 1024; // 基础大小 let baseSize = 1280; // 大于文件限制,手动压缩 if (img.size > fileLimit) { return compressImg({src:img.path, size:img.size, canvas, baseSize, isIOS, pixelRatio}).then(response => { return Promise.resolve(response); }); } return Promise.resolve(img.path); }); } /** * @description 根据图片的大小选择压缩的方式 * @param {string} src 图片的path * @param {number} size 图片的大小 * @param {object} canvas canvas对象 * @param {number} baseSize 基础尺寸 * @param {boolean} isIOS 是否是IOS系统 * @returns {Promise} 返回Promise对象 */ function compressImg({src, size, canvas, baseSize, isIOS, pixelRatio}) { return new Promise((resolve, reject) => { wx.getImageInfo({ src }).then(res => { let imgWidth = res.width; let imgHeight = res.height; if (imgWidth <= 4096 && imgHeight <= 4096) { // 小于4096使用canvas压缩 canvasToImage({src, size, imgWidth, imgHeight, canvas, baseSize, isIOS, pixelRatio}).then(response => { resolve(response); }); } else { // 超过4096使用强制压缩 compressImage(src, size, isIOS).then(response => { resolve(response); }); } }).catch(err => { // 使用强制压缩 compressImage(src, size, isIOS).then(response => { resolve(response); }); }); }); } /** * @description 使用wx.compressImage压缩图片 * @param {string} src 图片的path * @param {number} size 图片的大小 * @param {boolean} isIOS 是否是IOS系统 * @returns {Promise} 返回Promise对象 */ function compressImage(src, size, isIOS) { return new Promise((resolve, reject) => { let quality = 100; if (isIOS) { quality = 0.1; } else { let temp = 30 - (size / 1024 / 1024); quality = temp < 10 ? 10 : temp; } wx.compressImage({ src, quality, success: (res) => { resolve(res.tempFilePath); }, fail: () => { // 压缩失败返回传递的图片src resolve(src); } }); }); } /** * @description 使用canvans压缩图片 * @param {string} src 图片的path * @param {number} size 图片的大小 * @param {number} imgWidth 图片的宽度 * @param {number} imgHeight 图片的高度 * @param {object} canvas canvas对象 * @param {number} baseSize 基础尺寸 * @param {boolean} isIOS 是否是IOS系统 * @param {number} pixelRatio 设备像素比 * @returns {Promise} 返回Promise对象 */ function canvasToImage({src, size, imgWidth, imgHeight, canvas, baseSize, isIOS, pixelRatio}) { return new Promise((resolve, reject) => { if (!canvas) { compressImage(src, size).then(res => { resolve(res); }); return; } // 设置canvas宽度和高度 let canvasWidth = 0; let canvasHeight = 0; let quality = 1; // 图片的宽度和高度都小于baseSize,宽高不变 if (imgWidth <= baseSize && imgHeight <= baseSize) { canvasWidth = imgWidth; canvasHeight = imgHeight; quality = 0.3; } else { let compareFlag = true; // 图片的一边大于baseSize,宽高不变 if (pixelRatio > 2 && (imgWidth > baseSize || imgHeight > baseSize) && (imgWidth < baseSize || imgHeight < baseSize)) { canvasWidth = imgWidth; canvasHeight = imgHeight; quality = 0.3; } else { // 按照原图的宽高比压缩 compareFlag = pixelRatio > 2 ? (imgWidth > imgHeight) : (imgWidth > imgHeight); // 宽比高大,宽按基准比例缩放,高设置为基准值,高比宽大,高按基准比例缩放,宽设置为基准值。 canvasWidth = compareFlag ? parseInt(imgWidth / (imgHeight / baseSize)) : baseSize; canvasHeight = compareFlag ? baseSize : parseInt(imgHeight / (imgwidth / baseSize)); quality = 0.9; } } let pic = canvas.createImage(); pic.src = src; pic.onerror = function () { // 加载失败使用强制压缩 compressImage(src, size, isIOS).then(response => { resolve(response); }); } pic.onload = function () { // 获取绘画上下文 let ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.drawImage(pic, 0, 0, canvasWidth, canvasHeight); // 导出图片 wx.canvasToTempFilePath({ canvas, width: canvasWidth, height: canvasHeight, destHeight: canvasHeight, destWidth: canvasWidth, fileType:'jpg', quality, success: (res) => { resolve(res.tempFilePath); }, fail: (err) => { // 压缩失败使用强制压缩 compressImage(src, size, isIOS).then(response => { resolve(response); }); } }); } }); } /** * @description 循环压缩图片 * @param {object} img 包含path:图片的path,size:图片的大小 * @param {object} canvas canvas对象 * @param {number} fileLimit 文件大小限制 * @returns {Promise} 返回Promise对象 */ async function cycleCompressImg(img, canvas, fileLimit) { let fileSystemManager = wx.getFileSystemManager(); function getFileInfoPromise(src) { return new Promise((resolve, reject) => { fileSystemManager.getFileInfo({ filePath: src, success: (res) => { resolve(res); }, fail: (err) => { reject(err); } }); }); } let size = await getFileInfoPromise(img.path).size; let path = img.path; while (size > fileLimit) { path = await _compressImage(img, canvas, fileLimit); } return path; } module.exports = { _compressImage, cycleCompressImg }; 使用 在需要使用的页面中,引入上面的代码。 由于在压缩中使用canvas因此需要在wxml文件中添加canvas元素 <view class="upload"> <view class="imageList"> <block wx:if="{{uploadedFilePaths && uploadedFilePaths.length}}"> <view class="imageList_item" wx:for="{{uploadedFilePaths}}" wx:key="index"> <image src="{{item}}" class="upload_file"></image> <view class="close" bindtap="handleClose" data-index="{{index}}"> <text class="flow">x</text> </view> </view> </block> <view wx:if="{{uploadedFilePaths.length < maxCount}}" class="camera" bindtap="handleCameraTap"> <canvas type="2d" id="uploadCanvas" class="canvas"></canvas> </view> </view> </view> 样式 /* components/upload/index.wxss */ .upload{ margin-top:20rpx; } .camera { width: 100rpx; height: 100rpx; border: 1px solid #d9d9d9; display: flex; align-items: center; justify-content: center; border-radius: 12rpx; background-color: #fff; position: relative; } .imageList { display: flex; align-items: center; } .imageList_item { border-radius: 12rpx; position: relative; width: 100rpx; height: 100rpx; margin-right: 20rpx; } .close { width: 32rpx; height: 32rpx; background-color: rgba(90, 90, 90, 0.3); border-radius: 50%; display: flex; align-items: center; justify-content: center; position: absolute; right: 0; top: 0; font-size: 24rpx; color: #fff; } .flow { display: flow-root; height: 100%; line-height: 100%; } .canvas { width: 100rpx; height: 100rpx; opacity: 0; position: absolute; z-index: -1; display: none; } .upload_img{ width: 48rpx; height: 48rpx; border-radius:12rpx; } .upload_file{ width: 100rpx; height: 100rpx; border-radius: 12rpx; } 然后在js文件中,调用选择图片的api,由于版本的更迭使用的api也在变化,因此为了能够方便使用把微信小程序中目前常用的选择图片的api封装成一个函数,代码如下: // chooseImage.js /** * @description 选择图片 * @param {object} options 选择图片的参数对象 * @returns {Promise} 返回Promise对象 */ function chooseImage(options) { options = options || {}; return wx.getSystemInfo().then(res => { let { system } = res; let isIOS = /ios/gi.test(system); let sizeType = isIOS ? ['compressed'] : ['origin', 'compressed']; if (wx.canIUse('chooseImage')) { return new Promise((resolve, reject) => { wx.chooseImage({ count: options.count || 9, sizeType: options.sizeType || sizeType, sourceType: options.sourceType || ['album', 'camera'], success: (res) => { resolve(res); }, fail: (err) => { reject(err); } }); }); } else if (wx.canIUse('chooseMedia')) { return new Promise((resolve, reject) => { wx.chooseMedia({ count: options.count || 9, mediaType: ['image'], sourceType: options.sourceType || ['album', 'camera'], success: (res) => { res.tempFiles = res.tempFiles.map(item => { item.path = item.tempFilePath; return item; }); resolve(res); }, fail: (err) => { reject(err); } }); }); } }); } module.exports = chooseImage; 引入上面的compressImage.js和chooseImage.js的 代码 const chooseImage = require('./chooseImage'); const { _compressImage } = require('./compressImage'); Page({ data:{ // 保存上传的文件路径 uploadFilePaths:[], // 上传的最大文件数量 maxCount:5, // 文件大小限制,单位kb,超过限制压缩文件 limitSize:1024 }, // 上传图片 uploadImg() { wx.getSetting().then(res => {}); if (this.data.uploadedFilePaths.length < this.data.maxCount) { const appAuthorizeSetting = wx.getSystemInfoSync(), cameraAuthorized = appAuthorizeSetting.cameraAuthorized, albumAuthorized = appAuthorizeSetting.albumAuthorized; // 相册或相机没有授权,先设置权限 if ((albumAuthorized !== undefined && !albumAuthorized) || !cameraAuthorized) { wx.openAppAuthorizeSetting().then(auth => {}).catch(e => {}); } else { let { maxCount } = this.data; // 授权后选择图片 chooseImage({ count: maxCount }).then(res => { let { tempFiles, } = res; if (tempFiles.length <= maxCount) { if ((tempFiles.length + this.data.uploadedFilePaths.length) > maxCount) { showToast({ title: '最多上传' + maxCount + '张图片' }); return ; } } else { showToast({ title: '最多上传' + maxCount + '张图片' }); return ; } this.getCanvas('uploadCanvas').then(canvas => { if (canvas) { let proArr = tempFiles.map(item => { return compressImage._compressImage(item, canvas, this.data.limitSize); }); Promise.all(proArr).then(res => { this.data.uploadedFilePaths = this.data.uploadedFilePaths.concat(res); this.setData({ uploadedFilePaths: this.data.uploadedFilePaths }); this.triggerEvent('upload', { value: this.data.uploadedFilePaths }); }); } }); }).catch(err => {}); } } }, // 获取canvas,用于压缩图片 getCanvas(id) { return new Promise((resolve, reject) => { let query = wx.createSelectorQuery(); query.in(this); query.select(`#${id}`).fields({ node: true, size: true }).exec((res) => { if (res[0]) { resolve(res[0].node); } else { resolve(null); } }); }); }, }) 每种图片处理方式都有其突出的优势,结合多种方式能够最优地解决问题,适用于目标场景,便利用户上传图片的体验。 ———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/qq_40850839/article/details/131287452 该文章在 2024/3/24 16:56:32 编辑过 |
关键字查询
相关文章
正在查询... |