uniapp小程序图片批量压缩并保存到相册

150 阅读2分钟

uni-app 小程序 中实现 多图选择 → 压缩 → 保存相册 的功能。


一、介绍

  • 压缩参数设置:用户通过滑块设置压缩质量和缩放比例
  • 保存到相册:压缩完成后,保存到手机相册
  • 效果预览图片压缩效果.png
  • 在线体验mp.jpg

二、核心逻辑

1. 静态页面

<canvas
  canvas-id="compressCanvas"
  id="compressCanvas"
  :style="`position:absolute;left:-9999px;width:${canvasWidth}px;height:${canvasHeight}px;`"
>
</canvas>

2. 选择图片

selectImage() {
  uni.chooseImage({
    count: 9,
    sourceType: ['album', 'camera'],
    success: (res) => {
      this.imageList = this.imageList.concat(res.tempFilePaths);
    }
  });
}

相册相机 选择,最多可选 9 张。


3. 批量压缩与进度提示

async startCompress() {
  if (this.imageList.length === 0) return;

  this.compressProgress = 0;
  let completed = 0;

  for (const img of this.imageList) {
    const compressed = await this.compressImage(img);
    await this.saveToAlbum(compressed);
    completed++;
    this.compressProgress = Math.floor((completed / this.imageList.length) * 100);
  }

  uni.showToast({ title: '已保存到相册!', icon: 'success' });
}

4. 压缩图片

压缩采用 Canvas 绘制方式,先根据设置的缩放比例计算目标宽高,再通过 canvasToTempFilePath 导出。

compressImage(imgPath) {
    return new Promise((resolve, reject) => {
        uni.getImageInfo({
            src: imgPath,
            success: (imgInfo) => {
                // 目标宽高(根据缩放比例计算)
                const targetWidth = Math.max(1, Math.floor(imgInfo.width * (this.size / 100)));
                const targetHeight = Math.max(1, Math.floor(imgInfo.height * (this.size / 100)));

                // 动态修改 canvas 实际大小
                this.canvasWidth = targetWidth;
                this.canvasHeight = targetHeight;
                // 延时 300ms,等待 canvas 宽高改变
                setTimeout(() => {
                    // 创建画布上下文
                    const ctx = uni.createCanvasContext('compressCanvas', this);

                    // 清理画布
                    ctx.clearRect(0, 0, targetWidth, targetHeight);

                    // 绘制整张图片 → 覆盖整个画布
                    ctx.drawImage(imgPath, 0, 0, targetWidth, targetHeight);


                    // 必须等 draw 执行完,再导出
                    ctx.draw(false, () => {
                        uni.canvasToTempFilePath({
                            canvasId: 'compressCanvas',
                            x: 0,
                            y: 0,
                            width: targetWidth,      // 截取区域
                            height: targetHeight,    // 截取区域
                            destWidth: targetWidth,   // 导出宽
                            destHeight: targetHeight, // 导出高
                            fileType: 'jpg',
                            quality: this.quality / 100,
                            success: (res) => {
                                resolve({
                                    originalPath: imgPath,
                                    compressedPath: res.tempFilePath,
                                    width: targetWidth,
                                    height: targetHeight
                                });
                            },
                            fail: (err) => {
                                console.error('导出失败', err);
                                reject(err);
                            }
                        }, this);
                    });
                }, 300);
            },
            fail: (err) => reject(err)
        });
    });
}

5. 保存到相册

压缩后的临时文件路径可以直接用 uni.saveImageToPhotosAlbum 保存。

saveToAlbum(filePath) {
  return new Promise((resolve, reject) => {
    uni.saveImageToPhotosAlbum({
      filePath,
      success: () => resolve(filePath),
      fail: reject
    });
  });
}

五、注意事项

  1. 图片被裁剪:原因是canvas宽高与图片不一致,compressImage方法中使用setTimeout等待canvas的宽高生效,确保图片完整(nextTick不行,暂不清楚原因 )。
  2. 图片未保存到相册:小程序预览时没问题,线上无法保存图片到相册,需要在微信公众平台完善用户隐私协议。