项目是用uniapp开发的,当时只是做App端,后来项目扩展到H5端, uniapp框架可以跨平台所以移动端和H5使用的是一套代码
上传头像的时候要求图片的大小在2MB一下,所以要压缩图片,App端当时使用的是uni.compressImage(OBJECT)压缩的(详情见:https://uniapp.dcloud.net.cn/api/media/image.html#compressimage 但是H5不兼容;
先搞定H5的压缩吧!网上一搜一大把
/** * H5压缩 二分查找算法来找到一个合适的图像质量系数,使得压缩后的图片文件大小接近于目标大小* @param {Object} imgSrc 图片url * @param {Object} callback 回调设置返回值 * */export function compressH5(fileItem, targetSizeKB, initialQuality = 1.0) {const maxQuality = 1.0;const minQuality = 0.0;const tolerance = 0.01; // 根据需要调整公差return new Promise((resolve, reject) => {const binarySearch = (min, max) => {const midQuality = (min + max) / 2;const reader = new FileReader();reader.readAsDataURL(fileItem);reader.onload = function () {const img = new Image();img.src = this.result;img.onload = function () {const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');canvas.width = img.width;canvas.height = img.height;ctx.clearRect(0, 0, canvas.width, canvas.height);ctx.drawImage(img, 0, 0, canvas.width, canvas.height);// 使用异步的 toBlob 方法canvas.toBlob(async (blob) => {const fileSizeKB = blob.size / 1024;if (Math.abs(fileSizeKB - targetSizeKB) < tolerance || max - min < tolerance) {// 当前质量足够接近目标大小,使用当前质量解析resolve(URL.createObjectURL(blob));} else if (fileSizeKB > targetSizeKB) {// 如果文件大小太大,降低质量,继续二分查找binarySearch(min, midQuality);} else {// 如果文件大小太小,增加质量,继续二分查找binarySearch(midQuality, max);}}, 'image/jpeg', midQuality);};};reader.onerror = function (error) {reject(error);};};// 开始二分查找binarySearch(minQuality, maxQuality);});}
调用方法
chooseImg1() {uni.chooseImage({count: 1, //默认9sizeType:['compressed'],success: (chooseImageRes) => {const tempFilePaths = chooseImageRes.tempFilePaths;const filePath = tempFilePaths[0];// 获取图片文件大小uni.getFileInfo({filePath: filePath,success: (fileInfo) => {const fileSize = fileInfo.size; // 图片文件大小,单位为B// 判断图片大小是否超过200KBif (fileSize > 200 * 1024) {//#ifdef H5//h5压缩图片const targetSizeKB = 150; // 设置目标文件大小,单位为KB,根据需求调整
compressH5(chooseImageRes.tempFiles[0],targetSizeKB).then(file => {console.log('file 222 = ', file)this.uploadCompressedImage(file);}).catch(err => {console.log('catch', err)})//#endif//#ifdef APP-PLUS || MP-WEIXIN// 如果超过200KB,进行压缩uni.compressImage({src: filePath,quality: 10, // 设置压缩质量(0-100)- 根据需求进行调整success: (compressRes) => {// 压缩成功后的逻辑this.uploadCompressedImage(compressRes.tempFilePath);},fail: (err) => {console.error('压缩图片失败:', err);uni.showToast({title: '压缩图片失败,请重试',icon: 'none'});}});//#endif} else {// 如果未超过200KB,直接上传原图this.uploadCompressedImage(filePath);}},fail: (err) => {console.error('获取文件信息失败:', err);uni.showToast({title: '获取文件信息失败,请重试',icon: 'none'});}});},fail: (err) => {console.error('选择图片失败:', err);uni.showToast({title: '选择图片失败,请重试',icon: 'none'});}});},
uploadCompressedImage(filePath) {uni.uploadFile({url: `${this.$VUE_APP_API_URL}/common/image/image/upload/faceImg`,filePath: filePath,name: 'file',header: {'Authorization': uni.getStorageSync('X-Token'), // 修改为你的访问令牌},success: (res) => {uni.hideLoading(); // 隐藏 loadingif (res.statusCode === 200) {console.log('上传成功:', res.data);const responseData = JSON.parse(res.data); // 解析返回的数据console.log(responseData,'responseData')// 处理上传成功后的逻辑if(responseData.code==200){this.contractImage1 = responseData.datauni.setStorageSync('faceUrl',this.contractImage1)}else{uni.$u.toast(responseData.msg);}} else {console.error('上传失败', res.statusCode);uni.showToast({title: res.msg, // 自定义错误信息icon: 'none'});}},fail: (err) => {console.error('上传失败:', err);uni.showToast({title: '上传失败,请重试',icon: 'none'});}});},
即能兼容h5也能兼容微信小程序和各个app
微信小程序图片压缩再次用二分查找压缩质量
// 压缩图片
export function compressImage(filePath, quality, successCallback, errorCallback) {uni.compressImage({src: filePath,quality: quality,success: (res) => {successCallback(res.tempFilePath);},fail: (err) => {errorCallback(err);}});
}// 二分查找压缩质量
export function binarySearchCompress(filePath, targetSize, low, high, successCallback, errorCallback) {if (low > high) {errorCallback("无法达到目标大小");return;}const mid = Math.floor((low + high) / 2);compressImage(filePath, mid, (tempFilePath) => {uni.getFileInfo({filePath: tempFilePath,success: (res) => {const currentSize = res.size;if (currentSize <= targetSize) {successCallback(tempFilePath);} else {// 递归调整压缩质量binarySearchCompress(filePath, targetSize, low, mid - 1, successCallback, errorCallback);}},fail: (err) => {errorCallback(err);}});}, (err) => {errorCallback(err);});
}
vue文件引用
//#ifdef APP-PLUS || MP-WEIXIN// 如果超过200KB,进行压缩const tempFilePath = chooseImageRes.tempFilePaths[0];const targetSizeKB = 200;// 将目标大小转换为字节数const targetSize = targetSizeKB * 1024;// 初始压缩质量范围const lowQuality = 1;const highQuality = 100;binarySearchCompress(tempFilePath, targetSize, lowQuality, highQuality, (compressedFilePath) => {console.log("压缩成功,压缩后图片路径:", compressedFilePath);this.uploadCompressedImage(compressedFilePath);}, (err) => {console.error("压缩失败:", err);})//#endif