小程序使用相册

微信小程序提供了丰富的相册操作 API,允许开发者访问用户的相册、拍照、选择图片、保存图片等功能。这些功能主要通过 wx.chooseMedia、wx.saveImageToPhotosAlbum 等 API 实现。

下面是使用的一些示例:

1,定义变量

  data: {
    // 选择的图片列表
    imageList: [],
    // 当前选中的图片索引
    currentIndex: 0,
    // 是否显示预览
    showPreview: false,
    // 加载状态
    loading: false,
    // 保存结果提示
    saveResult: ''
  },
  onLoad() {
    console.log('相册页面加载');
    this.checkAlbumPermission();
  },
  /**
   * 检查相册权限
   */
  checkAlbumPermission() {
    wx.getSetting({
      success: (res) => {
        if (!res.authSetting['scope.writePhotosAlbum']) {
          // 如果没有相册权限,提示用户授权
          this.requestAlbumPermission();
        }
      }
    });
  },
  /**
   * 请求相册权限
   */
  requestAlbumPermission() {
    wx.authorize({
      scope: 'scope.writePhotosAlbum',
      success: () => {
        console.log('相册权限授权成功');
      },
      fail: () => {
        console.log('相册权限授权失败');
        this.showPermissionGuide();
      }
    });
  },
  /**
   * 显示权限引导
   */
  showPermissionGuide() {
    wx.showModal({
      title: '权限提示',
      content: '需要相册权限才能保存图片,请在设置中开启权限',
      confirmText: '去设置',
      success: (res) => {
        if (res.confirm) {
          wx.openSetting({
            success: (res) => {
              console.log('打开设置成功', res);
            }
          });
        }
      }
    });
  },
  /**
   * 选择相册图片
   */
  chooseFromAlbum() {
    wx.chooseMedia({
      count: 9, // 最多选择9张
      mediaType: ['image'], // 只选择图片
      sourceType: ['album'], // 从相册选择
      maxDuration: 30,
      camera: 'back',
      success: (res) => {
        console.log('选择图片成功', res);

        const tempFiles = res.tempFiles;
        const newImageList = tempFiles.map((file, index) => ({
          tempFilePath: file.tempFilePath,
          size: file.size,
          width: file.width,
          height: file.height,
          thumbTempFilePath: file.thumbTempFilePath,
          id: Date.now() + index, // 生成唯一ID
          selected: false
        }));

        // 合并到现有列表
        this.setData({
          imageList: [...this.data.imageList, ...newImageList],
          saveResult: ''
        });

        wx.showToast({
          title: `成功选择${newImageList.length}张图片`,
          icon: 'success',
          duration: 2000
        });
      },
      fail: (err) => {
        console.error('选择图片失败', err);
        this.handleChooseError(err);
      }
    });
  },
  /**
   * 拍照并选择
   */
  takePhoto() {
    wx.chooseMedia({
      count: 1,
      mediaType: ['image'],
      sourceType: ['camera'], // 从相机拍照
      camera: 'back',
      success: (res) => {
        console.log('拍照成功', res);

        const file = res.tempFiles[0];
        const newImage = {
          tempFilePath: file.tempFilePath,
          size: file.size,
          width: file.width,
          height: file.height,
          thumbTempFilePath: file.thumbTempFilePath,
          id: Date.now(),
          selected: false,
          isFromCamera: true // 标记来自相机
        };

        this.setData({
          imageList: [newImage, ...this.data.imageList],
          saveResult: ''
        });

        wx.showToast({
          title: '拍照成功',
          icon: 'success',
          duration: 2000
        });
      },
      fail: (err) => {
        console.error('拍照失败', err);
        this.handleChooseError(err);
      }
    });
  },
  /**
   * 处理选择错误
   */
  handleChooseError(err) {
    let errorMsg = '选择图片失败';

    switch (err.errMsg) {
      case 'chooseMedia:fail auth deny':
        errorMsg = '没有相册访问权限';
        break;
      case 'chooseMedia:fail cancel':
        errorMsg = '用户取消了选择';
        break;
      case 'chooseMedia:fail no media':
        errorMsg = '没有找到图片';
        break;
      default:
        errorMsg = err.errMsg || errorMsg;
    }

    wx.showToast({
      title: errorMsg,
      icon: 'none',
      duration: 2000
    });
  },
  /**
   * 预览图片
   */
  previewImage(e) {
    const index = e.currentTarget.dataset.index;
    const urls = this.data.imageList.map(item => item.tempFilePath);

    wx.previewImage({
      current: urls[index],
      urls: urls,
      success: () => {
        console.log('预览图片成功');
      },
      fail: (err) => {
        console.error('预览图片失败', err);
        wx.showToast({
          title: '预览失败',
          icon: 'none'
        });
      }
    });
  },
  /**
   * 选择/取消选择图片
   */
  toggleSelect(e) {
    const index = e.currentTarget.dataset.index;
    const imageList = this.data.imageList.map((item, i) => {
      if (i === index) {
        return { ...item, selected: !item.selected };
      }
      return item;
    });

    this.setData({ imageList });
  },

  /**
   * 全选/取消全选
   */
  toggleSelectAll() {
    const allSelected = this.data.imageList.every(item => item.selected);
    const imageList = this.data.imageList.map(item => ({
      ...item,
      selected: !allSelected
    }));

    this.setData({ imageList });
  },

  /**
   * 获取选中的图片
   */
  getSelectedImages() {
    return this.data.imageList.filter(item => item.selected);
  },
  /**
   * 保存选中图片到相册
   */
  saveSelectedToAlbum() {
    const selectedImages = this.getSelectedImages();

    if (selectedImages.length === 0) {
      wx.showToast({
        title: '请先选择图片',
        icon: 'none',
        duration: 2000
      });
      return;
    }

    this.setData({ loading: true, saveResult: '' });

    // 批量保存
    this.batchSaveImages(selectedImages);
  },

  /**
   * 批量保存图片
   */
  async batchSaveImages(images) {
    let successCount = 0;
    let failCount = 0;
    const results = [];

    for (let i = 0; i < images.length; i++) {
      try {
        await this.saveSingleImage(images[i]);
        successCount++;
        results.push({ index: i, success: true });
      } catch (error) {
        failCount++;
        results.push({ index: i, success: false, error: error.message });
      }

      // 更新进度
      this.setData({
        saveResult: `保存进度: ${i + 1}/${images.length}`
      });
    }

    this.setData({ loading: false });
    this.showSaveResult(successCount, failCount, results);
  },

  /**
   * 保存单张图片
   */
  saveSingleImage(image) {
    return new Promise((resolve, reject) => {
      wx.saveImageToPhotosAlbum({
        filePath: image.tempFilePath,
        success: () => {
          console.log('保存图片成功', image.tempFilePath);
          resolve();
        },
        fail: (err) => {
          console.error('保存图片失败', err);
          reject(new Error(this.getSaveErrorMsg(err)));
        }
      });
    });
  },

  /**
   * 获取保存错误信息
   */
  getSaveErrorMsg(err) {
    switch (err.errMsg) {
      case 'saveImageToPhotosAlbum:fail auth deny':
        return '没有相册写入权限';
      case 'saveImageToPhotosAlbum:fail cancel':
        return '用户取消了保存';
      case 'saveImageToPhotosAlbum:fail no image':
        return '图片文件不存在';
      default:
        return err.errMsg || '保存失败';
    }
  },

  /**
   * 显示保存结果
   */
  showSaveResult(successCount, failCount, results) {
    let title = '';
    if (failCount === 0) {
      title = `成功保存${successCount}张图片`;
    } else if (successCount === 0) {
      title = `保存失败,${failCount}张图片未保存`;
    } else {
      title = `成功${successCount}张,失败${failCount}张`;
    }

    this.setData({
      saveResult: title
    });

    wx.showModal({
      title: '保存结果',
      content: title,
      showCancel: false,
      success: () => {
        // 清空选择状态
        const imageList = this.data.imageList.map(item => ({
          ...item,
          selected: false
        }));
        this.setData({ imageList });
      }
    });

    // 记录详细结果
    console.log('保存详细结果:', results);
  },
  /**
   * 压缩图片
   */
  compressImage(imagePath) {
    return new Promise((resolve, reject) => {
      wx.compressImage({
        src: imagePath,
        quality: 80, // 压缩质量 0-100
        success: (res) => {
          console.log('图片压缩成功', res);
          resolve(res.tempFilePath);
        },
        fail: (err) => {
          console.error('图片压缩失败', err);
          reject(err);
        }
      });
    });
  },

  /**
   * 获取图片信息
   */
  getImageInfo(imagePath) {
    return new Promise((resolve, reject) => {
      wx.getImageInfo({
        src: imagePath,
        success: (res) => {
          console.log('图片信息获取成功', res);
          resolve(res);
        },
        fail: (err) => {
          console.error('图片信息获取失败', err);
          reject(err);
        }
      });
    });
  },
  /**
   * 批量压缩并保存
   */
  async compressAndSaveSelected() {
    const selectedImages = this.getSelectedImages();

    if (selectedImages.length === 0) {
      wx.showToast({
        title: '请先选择图片',
        icon: 'none'
      });
      return;
    }

    this.setData({ loading: true });

    try {
      // 先压缩所有图片
      const compressedPaths = [];
      for (const image of selectedImages) {
        const compressedPath = await this.compressImage(image.tempFilePath);
        compressedPaths.push(compressedPath);
      }

      // 保存压缩后的图片
      for (let i = 0; i < compressedPaths.length; i++) {
        await this.saveSingleImage({ tempFilePath: compressedPaths[i] });

        this.setData({
          saveResult: `压缩保存进度: ${i + 1}/${compressedPaths.length}`
        });
      }

      this.setData({ loading: false });
      this.showSaveResult(compressedPaths.length, 0, []);

    } catch (error) {
      this.setData({ loading: false });
      wx.showToast({
        title: '压缩保存失败',
        icon: 'none'
      });
    }
  },

  /**
   * 删除选中图片
   */
  deleteSelected() {
    const selectedImages = this.getSelectedImages();

    if (selectedImages.length === 0) {
      wx.showToast({
        title: '请先选择要删除的图片',
        icon: 'none'
      });
      return;
    }

    wx.showModal({
      title: '确认删除',
      content: `确定要删除选中的${selectedImages.length}张图片吗?`,
      success: (res) => {
        if (res.confirm) {
          const remainingImages = this.data.imageList.filter(item => !item.selected);
          this.setData({
            imageList: remainingImages,
            saveResult: `已删除${selectedImages.length}张图片`
          });

          wx.showToast({
            title: '删除成功',
            icon: 'success'
          });
        }
      }
    });
  },

  /**
   * 清空所有图片
   */
  clearAllImages() {
    if (this.data.imageList.length === 0) {
      wx.showToast({
        title: '没有图片可清空',
        icon: 'none'
      });
      return;
    }

    wx.showModal({
      title: '确认清空',
      content: '确定要清空所有图片吗?',
      success: (res) => {
        if (res.confirm) {
          this.setData({
            imageList: [],
            saveResult: '已清空所有图片'
          });

          wx.showToast({
            title: '清空成功',
            icon: 'success'
          });
        }
      }
    });
  },
  /**
   * 分享图片
   */
  onShareAppMessage() {
    const selectedImages = this.getSelectedImages();

    if (selectedImages.length > 0) {
      return {
        title: `分享${selectedImages.length}张图片`,
        path: '/pages/album/album',
        imageUrl: selectedImages[0].thumbTempFilePath
      };
    }

    return {
      title: '分享我的相册',
      path: '/pages/album/album'
    };
  },

  /**
   * 下拉刷新
   */
  onPullDownRefresh() {
    console.log('下拉刷新');

    // 模拟刷新操作
    setTimeout(() => {
      this.setData({
        saveResult: '刷新完成'
      });
      wx.stopPullDownRefresh();

      wx.showToast({
        title: '刷新成功',
        icon: 'success'
      });
    }, 1000);
  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onReachBottom() {
    console.log('上拉加载更多');
    // 可以在这里实现加载更多图片的逻辑
  },

  /**
   * 用户点击右上角分享
   */
  onShareTimeline() {
    return {
      title: '我的小程序相册',
      imageUrl: '/images/share.jpg'
    };
  },

  /**
   * 错误处理统一函数
   */
  handleError(error, operation = '操作') {
    console.error(`${operation}失败:`, error);

    wx.showToast({
      title: `${operation}失败`,
      icon: 'none',
      duration: 2000
    });
  },

  /**
   * 性能优化:图片懒加载
   */
  onImageLoad(e) {
    const index = e.currentTarget.dataset.index;
    console.log(`图片${index}加载完成`);

    // 可以在这里添加图片加载完成的处理逻辑
  },

  /**
   * 性能优化:图片加载失败
   */
  onImageError(e) {
    const index = e.currentTarget.dataset.index;
    console.error(`图片${index}加载失败`);

    // 可以在这里设置默认图片或重试逻辑
  }
});

这是一个 WXML 文件:

<!-- pages/album/album.wxml -->
<view class="container">
  <!-- 操作按钮区域 -->
  <view class="action-buttons">
    <button bindtap="chooseFromAlbum">选择相册</button>
    <button bindtap="takePhoto">拍照</button>
    <button bindtap="toggleSelectAll">全选/取消</button>
    <button bindtap="saveSelectedToAlbum">保存选中</button>
    <button bindtap="compressAndSaveSelected">压缩保存</button>
    <button bindtap="deleteSelected">删除选中</button>
    <button bindtap="clearAllImages">清空全部</button>
  </view>

  <!-- 状态显示 -->
  <view class="status-info">
    <text>已选择: {{imageList.length}} 张图片</text>
    <text>选中: {{getSelectedImages().length}} 张</text>
    <text>{{saveResult}}</text>
  </view>

  <!-- 图片列表 -->
  <view class="image-grid">
    <view
      wx:for="{{imageList}}"
      wx:key="id"
      class="image-item {{item.selected ? 'selected' : ''}}"
      bindtap="toggleSelect"
      data-index="{{index}}"
    >
      <image
        src="{{item.thumbTempFilePath}}"
        mode="aspectFill"
        bindload="onImageLoad"
        binderror="onImageError"
        data-index="{{index}}"
        bindtap="previewImage"
      />
      <view class="image-mask" wx:if="{{item.selected}}">✓</view>
    </view>
  </view>

  <!-- 加载指示器 -->
  <view wx:if="{{loading}}" class="loading">
    <text>处理中...</text>
  </view>
</view>

主要功能说明

  1. ​ 图片选择功能 ​

chooseFromAlbum(): 从相册选择图片

takePhoto(): 拍照获取图片

支持多选(最多 9 张)

错误处理和权限检查

  1. ​ 图片管理功能 ​

toggleSelect(): 选择/取消选择单张图片

toggleSelectAll(): 全选/取消全选

deleteSelected(): 删除选中图片

clearAllImages(): 清空所有图片

  1. ​ 图片保存功能 ​

saveSelectedToAlbum(): 保存选中图片到相册

compressAndSaveSelected(): 压缩后保存

批量保存,支持进度显示

详细的错误处理和结果反馈

  1. ​ 图片处理功能 ​

previewImage(): 预览大图

compressImage(): 图片压缩

getImageInfo(): 获取图片信息

  1. ​ 权限管理 ​

自动检查相册权限

引导用户授权

友好的错误提示

  1. ​ 用户体验优化 ​

加载状态显示

操作结果反馈

错误处理机制

性能优化(懒加载等)

这个示例提供了完整的相册操作功能,可以根据实际需求进行裁剪和扩展。

通关密语:相册