photoPicker如何筛选某个相册的图片和视频
=========================

#### 问题现象

目前photoPicker选择图片和视频会获取到所有的相册，如何获取某个相册的图片或者视频？

#### 背景知识

*   [PickerOptions](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ohos-file-photopickercomponent#pickeroptions)：Picker配置选项，其中appAlbumFilters（API23）方法可以指定显示bundle name对应的相册内容。
*   [getAlbums](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-photoaccesshelper#getalbums-2)：根据检索选项和相册类型获取相册，AlbumType、AlbumSubtype两个属性在API23支持设置来源相册。

#### 解决方案

方案一（应用相册）：在使用PhotoPickerComponent时候通过配置PickerOptions的appAlbumFilters选项来筛选指定相册的内容，appAlbumFilters需要设置对应应用的bundle name，可以设置多个应用的bundle name进行显示。示例代码如下：

_// 选择指定应用的相册的内容(API 23)_
this.pickerOptions.appAlbumFilters = \['xxx'\]; _// 此处需要替换为需要筛选的应用相册的bundlename_

方案二（来源相册）：getAlbums获取应用相册，需要设置[AlbumType](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-e#albumtype)为SOURCE，[AlbumSubtype](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-e#albumsubtype)为SOURCE\_GENERIC类型，通过[getAlbums](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-photoaccesshelper#getalbums-2)筛选来源相册，然后通过[getAssets](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-photoaccesshelper#getassets-1)获取对应相册的内容。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/7d/v3/TTxxv84CTuGGH1aAWzJYAQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T134135Z&HW-CC-Expire=86400&HW-CC-Sign=6C27A25BA5EA3090CB769E2760EEB7946BB5361F4FCC205EF7380F402A0C5C92)

此方案需要申请[ohos.permission.READ\_IMAGEVIDEO](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/restricted-permissions#ohospermissionread_imagevideo)权限，[申请受限权限](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/declare-permissions-in-acl)以后在代码中配置相关权限可参考[声明权限](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/declare-permissions)。

示例代码如下：

async photoPickerByAlbumName() {
  let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
  let albunmPredicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
  albunmPredicates.equalTo(photoAccessHelper.AlbumKeys.ALBUM\_NAME, 'Test'); _// Test需要替换为需要筛选的相册名字_
  const fetchOps: photoAccessHelper.FetchOptions = {
    fetchColumns: \[\],
    predicates: albunmPredicates
  };
  let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
  let fetchOptions: photoAccessHelper.FetchOptions = {
    fetchColumns: \[\],
    predicates: predicates
  };
  try {
    let albumFetchResult: photoAccessHelper.FetchResult<photoAccessHelper.Album> =
      await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.SOURCE,
        photoAccessHelper.AlbumSubtype.SOURCE\_GENERIC, fetchOps); _// 此处搜索范围是应用创建的相册，开发者可根据实际情况修改type_
    let album: photoAccessHelper.Album = await albumFetchResult.getFirstObject();
    let photoFetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
      await album.getAssets(fetchOptions);
    let photoAsset: photoAccessHelper.PhotoAsset = await photoFetchResult.getFirstObject();
    this.uri = photoAsset.uri;
    photoFetchResult.close();
    albumFetchResult.close();
  } catch (err) {
    console.error('photo failed with err: ' + err);
  }
}

完整代码如下：

index.ets：

import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  async routePage(routeUrl: string) {
    this.getUIContext()
      .getRouter()
      .pushUrl({
        url: routeUrl,
        params: {
          data1: 'message',
          data2: {
            data3: \[123, 456, 789\]
          }
        }
      })
      .then(() => {
        console.info('succeeded');
      })
      .catch((error: BusinessError) => {
        console.error(\`pushUrl failed, code is ${error.code}, message is ${error.message}\`);
      });
  }

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Button('byBundleName')
      .margin({ top: 20 })
      .onClick(() => {
        this.routePage('pages/photoPickerByBundleName');
      });

      Button('byAlbumName')
      .margin({ top: 20 })
      .onClick(() => {
        this.routePage('pages/photoPickerByAlbumName');
      });
    }
    .width('100%')
    .height('100%');
  }
}

photoPickerByAlbumName.ets：

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { dataSharePredicates } from '@kit.ArkData';
import { abilityAccessCtrl, common, PermissionRequestResult } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct PhotoPickerByAlbumName {
  @State uri: string = '';
  async photoPickerByAlbumName() {
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
    let albunmPredicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
    albunmPredicates.equalTo(photoAccessHelper.AlbumKeys.ALBUM\_NAME, 'Test'); _// Test需要替换为需要筛选的相册名字_
    const fetchOps: photoAccessHelper.FetchOptions = {
      fetchColumns: \[\],
      predicates: albunmPredicates
    };
    let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
    let fetchOptions: photoAccessHelper.FetchOptions = {
      fetchColumns: \[\],
      predicates: predicates
    };
    try {
      let albumFetchResult: photoAccessHelper.FetchResult<photoAccessHelper.Album> =
        await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.SOURCE,
          photoAccessHelper.AlbumSubtype.SOURCE\_GENERIC, fetchOps); _// 此处搜索范围是应用创建的相册，开发者可根据实际情况修改type_
      let album: photoAccessHelper.Album = await albumFetchResult.getFirstObject();
      let photoFetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
        await album.getAssets(fetchOptions);
      let photoAsset: photoAccessHelper.PhotoAsset = await photoFetchResult.getFirstObject();
      this.uri = photoAsset.uri;
      photoFetchResult.close();
      albumFetchResult.close();
    } catch (err) {
      console.error('photo failed with err: ' + err);
    }
  }
  aboutToAppear() {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    atManager.requestPermissionsFromUser(context,
      \['ohos.permission.READ\_IMAGEVIDEO', 'ohos.permission.WRITE\_IMAGEVIDEO'\],
      (err: BusinessError, data: PermissionRequestResult) => {
        if (err) {
          console.error(\`requestPermissionsFromUser fail, code: ${err.code}, message: ${err.message}\`);
        } else {
          this.photoPickerByAlbumName();
          console.info(\`requestPermissionsFromUser success, result: ${data}\`);
          console.info(\`requestPermissionsFromUser data permissions: + ${data.permissions}\`);
          console.info(\`requestPermissionsFromUser data authResults: + ${data.authResults}\`);
          console.info(\`requestPermissionsFromUser data dialogShownResults: + ${data.dialogShownResults}\`);
        }
      });
  }

  build() {
    RelativeContainer() {
      Image(this.uri)
        .height(100)
        .width(100);
    }
    .margin({ top: 20 })
    .height('100%')
    .width('100%');
  }
}

photoPickerByBundleName.ets：

import {
  PhotoPickerComponent,
  PickerController,
  PickerOptions,
  BaseItemInfo,
  DataType,
  ItemInfo,
  ItemType,
  ClickType,
  PhotoBrowserRange,
  ReminderMode,
  photoAccessHelper
} from '@kit.MediaLibraryKit';

@Entry
@Component
struct PhotoPickerComponentDemo {
  _// 组件初始化时设置参数信息。_
  pickerOptions: PickerOptions = new PickerOptions();
  _// 组件初始化完成后，可控制组件部分行为。_
  @State pickerController: PickerController = new PickerController();
  _// 宫格图内已选择的图片uri数组。_
  @State selectUris: Array<string> = new Array<string>();
  _// 目前选择的图片uri。_
  @State currentUri: string = '';
  _// 标识当前是否显示大图页面，false表示不显示大图页面，true表示显示大图页面__。_
  @State isBrowserShow: boolean = false;

  aboutToAppear() {
    _// 设置picker宫格页可选择的媒体文件类型，这里设置图片和视频类型。_
    this.pickerOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE\_VIDEO\_TYPE;
    _// 设置宫格页内资源的最大选择数量，示例设置为5。_
    this.pickerOptions.maxSelectNumber = 5;
    _// 选择数量达到最大时的提示方式，示例设置为弹窗提示。_
    this.pickerOptions.maxSelectedReminderMode = ReminderMode.TOAST;
    _// 设置picker页面内是否需要展示搜索框，false为不展示。_
    this.pickerOptions.isSearchSupported = true;
    _// 将宫格页面内第一个宫格置为拍照按钮，false为不展示拍照按钮。_
    this.pickerOptions.isPhotoTakingSupported = true;
    _// 选择指定应用的相册的内容(API 23)_
    this.pickerOptions.appAlbumFilters = \['xxx'\]; _// 此处需要替换为需要筛选的应用相册的bundlename_
  }

  _// 资源被选中回调，返回资源的信息，以及选中方式。_
  _// 应用根据自己的业务来决定，资源是否勾选或者是否进入系统相机。_
  private onItemClicked(itemInfo: ItemInfo, clickType: ClickType): boolean {
    if (!itemInfo) {
      return false;
    }
    let type: ItemType | undefined = itemInfo.itemType;
    let uri: string | undefined = itemInfo.uri;
    if (type === ItemType.CAMERA) {
      _// 如果宫格页面第一个宫格的类型为ItemType.CAMERA，则是相机按钮。_
      _// 返回true则拉起系统相机；如果返回false应用可以自己拉起相机。_
      return true;
    } else {
      _// 如果是选中操作。_
      if (clickType === ClickType.SELECTED) {
        _// 应用做自己的业务处理。_
        if (uri) {
          this.selectUris.push(uri);
          this.pickerOptions.preselectedUris = \[...this.selectUris\];
        }
        _// 返回true则该宫格响应勾选，否则不响应勾选。_
        return true;
      } else {
        _// 如果是取消选中操作。_
        _// 应用做自己的业务处理。_
        if (uri) {
          this.selectUris = this.selectUris.filter((item: string) => {
            return item != uri;
          });
          this.pickerOptions.preselectedUris = \[...this.selectUris\];
        }
        _// 返回true则该宫格响应取消勾选，否则不响应取消勾选。_
        return true;
      }
    }
  }

 _ // 接收到该回调后，便可通过pickerController相关接口向picker发送数据，在此之前不生效。_
  private onPickerControllerReady(): void {
  }

  _// 退出大图时的回调。_
  private onExitPhotoBrowser(): boolean {
    this.isBrowserShow = false;
    return true;
  }
  _// 大图左右滑动的回调。_
  private onPhotoBrowserChanged(browserItemInfo: BaseItemInfo): boolean {
    this.currentUri = browserItemInfo.uri ?? '';
    return true;
  }
  build() {
    Flex({
      direction: FlexDirection.Column,
      alignItems: ItemAlign.Start
    }) {
      PhotoPickerComponent({
        pickerOptions: this.pickerOptions,
        onItemClicked: (itemInfo: ItemInfo, clickType: ClickType): boolean => this.onItemClicked(itemInfo, clickType),
        onPickerControllerReady: (): void => this.onPickerControllerReady(),
        pickerController: this.pickerController,
      });

      _// 这里模拟应用侧底部的选择栏。_
      if (this.isBrowserShow) {
        _// 已选择的图片缩略图。_
        Row() {
          ForEach(this.selectUris, (uri: string) => {
            if (uri === this.currentUri) {
              Image(uri)
                .height(50)
                .width(50)
                .onClick(() => {
                })
                .borderWidth(1)
                .borderColor('red');
            } else {
              Image(uri).height(50).width(50).onClick(() => {
                this.pickerController.setData(DataType.SET\_SELECTED\_URIS, this.selectUris);
                _// 点击底部缩略图，切换大图浏览的照片为点击的缩略图；本示例设置浏览范围为全部，包括图片和视频。_
                this.pickerController.setPhotoBrowserItem(uri, PhotoBrowserRange.ALL);
              });
            }
          }, (uri: string) => JSON.stringify(uri));
        }.alignSelf(ItemAlign.Center).margin(this.selectUris.length ? 10 : 0);
      } else {
        _// 进入大图，预览已选择的图片。_
        Button('预览')
          .width('33%')
          .alignSelf(ItemAlign.Start)
          .height('5%')
          .margin(10)
          .onClick(() => {
            if (this.selectUris.length > 0) {
              _// 切换picker组件至大图浏览模式浏览图片。_
              this.pickerController.setPhotoBrowserItem(this.selectUris\[0\], PhotoBrowserRange.SELECTED\_ONLY);
            }
          });
      }
    };
  }
}

#### 常见FAQ

Q：系统中创建了名为Test的相册，为什么点击byAlbumName按钮后还是报错“photo failed with err: System inner fail.”？

A：代码中筛选相册使用的类型分别是photoAccessHelper.AlbumType.SOURCE和photoAccessHelper.AlbumSubtype.SOURCE\_GENERIC，只能查找到由应用创建的相册。开发者可根据实际情况修改筛选的相册类型。