LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

【开发】微信小程序图片上传压缩代码及注意事项

Ccoffee
2024年3月24日 9:46 本文热度 715
上传图片是小程序常见的功能,例如点评类小程序邀请用户分享照片、电商类小程序要求商家上传商品照片。

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

小程序提供 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 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved