小程序图片上传压缩

阅读: 评论:0

小程序图片上传压缩

小程序图片上传压缩

上传图片是小程序常见的功能,例如点评类小程序邀请用户分享照片、电商类小程序要求商家上传商品照片。

伴随着照片像素越来越高,图片体积越来越大,小程序开发者需要压缩图片,否则将导致用户上传图片失败或加载时间过长等影响体验的情况。

小程序提供 wx.chooseMedia、wx.canvasToTempFilePath、wxpressImage 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,然后利用 wx.canvasToTempFilePath 接口转换成图片。

这种方法能够高效控制图片宽高尺寸以及压缩质量,非常适用于有图片要求的场景。

wx.canvasToTempFilePath({width: 50, // 画布区域的宽度height: 50,  // 画布区域的高度destWidth: 100,  // 输出图片的宽度destHeight: 100,  // 输出图片的高度canvasId: 'myCanvas',quality: 1,  // 图片质量0-1success(res) {console.pFilePath)}
});

但是这种方式也会存在一定的限制:

  • iOS 和安卓的压缩机制不同,需要进行合理兼容

  • 通过 Canvas 转换的图片存在略微色差

wxpressImage

开发者可以调用wxpressImage 接口直接压缩图片,而且支持选择压缩质量,不限制图片宽高尺寸,非常适用于处理特殊大小的图片。

wxpressImage({src: '', // 图片路径quality: 80 // 压缩质量 0-100
});

同时这种方式也需要考虑不同系统的压缩差异:

  • 在压缩到极限值时,iOS 压缩图画质不会随着压缩质量变小而变化

  • 在压缩质量小于 1 时,安卓系统输出的画质将不再变小

多方式结合处理

回顾常见的小程序业务场景,图片处理主要聚焦于用户上传图片、列表展示这 2 个环节,可以结合以上 3 个接口实现最佳图片处理方式,既能够利用接口自带的压缩功能,省时省力;又能够解决图片太大造成的压缩难题。

  1. 判断系统类型

判断当前系统是 iOS 系统还是安卓系统

function isIOS(){SystemInfo().then(res => {return /st(res.system);});
}
  1. 根据系统选择上传方式

iOS 系统:设置 sizeType 为 [‘compressed’],利用 iOS 压缩体系自动压缩

安卓系统:设置 sizeType 为 [‘original’, ‘compressed’],让用户自主选择上传原图或压缩图。

这种方式一方面利用接口自带的压缩能力; 另一方面如果图片宽高大于安卓能清晰压缩的值(例如40000),用户会预览到比较模糊的照片而选择上传原图

  1. 验证大小,手动压缩

当用户选择图片后,wx.chooseMedia 返回的 tempFiles 显示对应图片的大小。如果该图片大小大于限制值,则进行手动压缩。

  1. 根据宽高选择压缩方式

通过 wx.getImageInfo 获取图片的宽高:

如果宽度或高度大于 4096,调用 wxpressImage 强制压缩

如果宽度和高度都小于 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) {SystemInfo().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 => {solve(response);});}solve(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 使用wxpressImage压缩图片* @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;}wxpressImage({src,quality,success: (res) => {pFilePath);},fail: () => {// 压缩失败返回传递的图片srcresolve(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 = ateImage();pic.src = r = function () {// 加载失败使用强制压缩compressImage(src, size, isIOS).then(response => {resolve(response);});}load = function () {// 获取绘画上下文let ctx = Context('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) => {pFilePath);},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) => {FileInfo({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;
}ports = {_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 || {};SystemInfo().then(res => {let {system} = res;let isIOS = /st(system);let sizeType = isIOS ? ['compressed'] : ['origin', 'compressed'];if (wx.canIUse('chooseImage')) {return new Promise((resolve, reject) => {wx.chooseImage({count: unt || 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: unt || 9,mediaType: ['image'],sourceType: options.sourceType || ['album', 'camera'],success: (res) => {pFiles = pFiles.map(item => {item.path = pFilePath;return item;});resolve(res);},fail: (err) => {reject(err);}});});}});
}ports = chooseImage;

引入上面的compressImage.jschooseImage.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 ;}Canvas('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.at(res);this.setData({uploadedFilePaths: this.data.uploadedFilePaths});iggerEvent('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);}});});},
})

每种图片处理方式都有其突出的优势,结合多种方式能够最优地解决问题,适用于目标场景,便利用户上传图片的体验。

本文发布于:2024-01-29 17:34:51,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170652089617115.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:图片上传   程序
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23