PhotoPicker的常见使用问题
==================

#### 问题现象

HarmonyOS为开发者提供了PhotoViewPicker接口和PhotoPickerComponent组件，开发者可以使用这两种方式拉起媒体文件选择器，让用户自行选择媒体文件资源。本文总结了一些PhotoPicker的常见使用问题如下：

1.  PhotoViewPicker和PhotoPickerComponent的区别是什么，如何选择？
2.  PhotoPickerComponent存在哪些限制？
3.  使用Picker完成图片选择后返回的uri如何使用，直接使用该uri上传报错如何处理？

#### 背景知识

[使用Picker选择媒体库资源](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/photoaccesshelper-photoviewpicker)：当用户需要分享图片、视频等文件时，开发者可以通过特定接口拉起系统图库，让用户自行选择待分享的资源，完成分享。此接口本身无需申请权限，目前适用于界面UIAbility，使用窗口组件触发。

[使用PhotoPicker组件访问图片/视频](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/component-guidelines-photoviewpicker)：当应用需要读取用户图片时，开发者可以在应用界面中嵌入PhotoPickerComponent组件，在用户选择所需要的图片资源后，直接返回该图片资源，而不需要授予应用读取图片文件的权限，即可完成图片或视频文件的访问和读取。

#### 解决方案

1.  PhotoViewPicker和PhotoPickerComponent是HarmonyOS为开发者提供的选取图库媒体文件资源的两种方式，两者差异及适用场景如下：
    
    | 
    对比维度
    
     | 
    
    PhotoViewPicker
    
     | 
    
    PhotoPickerComponent
    
     |
    | :-- | :-- | :-- |
    | 
    
    本质类型
    
     | 
    
    系统接口（@ohos.photoAccess.photoAccessHelper）
    
     | 
    
    嵌入式组件（@ohos.file.PhotoPickerComponent）
    
     |
    | 
    
    调用方式
    
     | 
    
    异步拉起系统相册界面
    
     | 
    
    直接嵌入应用布局
    
     |
    | 
    
    界面交互
    
     | 
    
    跳转至独立系统相册界面
    
     | 
    
    内嵌在当前应用页面
    
     |
    | 
    
    可配置项
    
     | 
    
    [PhotoSelectOptions](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-class#photoselectoptions)
    
     | 
    
    [PickerOptions](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ohos-file-photopickercomponent#pickeroptions)
    
     |
    | 
    
    定制化能力
    
     | 
    
    使用固定系统界面
    
     | 
    
    可深度集成到自定义 UI（支持背景色/勾选框样式等配置）
    
     |
    | 
    
    适用场景
    
     | 
    
    需系统级相册界面
    
    要求文件类型过滤
    
    需重复选择同一文件
    
     | 
    
    避免页面跳转的沉浸式体验
    
    需与自定义 UI 深度集成
    
    简化交互流程（如勾选后直接编辑）
    
     |
    

2.  PhotoPickerComponent组件使用存在限制如下：
    *   不支持嵌套使用，用户使用PhotoPickerComponent选中媒体文件后，系统会将媒体文件的uri授权给应用，如果此时在Picker上方存在可点击事件，可能会对用户安全造成影响，因此PhotoPickerComponent上方覆盖设置了overlay属性的组件，将导致PhotoPickerComponent无法接受手势事件。
    *   PhotoPickerComponent不支持[同层渲染](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/web-same-layer)。
    *   PhotoPickerComponent不支持在[@ohos.inputMethod (输入法框架)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-inputmethod)中使用。

3.  使用Picker完成图片选择后，会直接返回该图片资源，系统出于安全考虑不允许直接对其进行上传，必须先存到沙箱，所以需要使用[copyFileSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-fs#fscopyfilesync)接口把文件资源读取到自己的沙箱目录中再进行操作。图库拷贝到沙箱参考代码：
    
    async copyFile2Sandbox(filePathString: string): Promise<boolean> {
      let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
      let resFile: fileIo.File | undefined;
      try {
        resFile = fileIo.openSync(filePathString, fileIo.OpenMode.READ\_ONLY);
      _  // 创建临时文件目录_
        let dateStr = (new Date().getTime()).toString();
        let newPath = context.cacheDir + \`/${dateStr + resFile.name}\`;
       _ // 拷贝系统图库图片到沙箱_
        fileIo.copyFileSync(resFile.fd, newPath);
        return true;
      } catch (error) {
        console.error(\`Failed to copy file: ${JSON.stringify(error)}\`);
        return false;
      } finally {
        if (resFile) {
          fileIo.closeSync(resFile);
        }
      }
    }
    
    此示例以PhotoPickerComponent为例，PhotoViewPicker同理，完整代码如下：
    
    import { common } from '@kit.AbilityKit';
    import { fileIo } from '@kit.CoreFileKit';
    import {
      PhotoPickerComponent,
      PickerController,
      PickerOptions,
      ItemInfo,
      ItemType,
      ClickType,
      ReminderMode,
      photoAccessHelper,
    } from '@kit.MediaLibraryKit';
    
    
    @Entry
    @Component
    struct Index {
      _// 组件初始化完成后，可控制组件部分行为。_
      @State pickerController: PickerController = new PickerController();
     _ // 组件初始化时设置参数信息。_
      pickerOptions: PickerOptions = new PickerOptions();
      _// 目前选择的图片。_
      currentUri: string = '';
    
    
      aboutToAppear() {
      _  // 设置picker宫格页数据类型_
        this.pickerOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE\_TYPE; _// 只显示图片_
     _   // 最大选择数量。_
        this.pickerOptions.maxSelectNumber = 1;
     _   // 超出最大选择数量时。_
        this.pickerOptions.maxSelectedReminderMode = ReminderMode.TOAST;
      }
    
    
     _ // 资源被选中回调，返回资源的信息，以及选中方式。_
      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) {
        _  // 点击相机item。_
          return true; _// 返回true则拉起系统相机，若应用需要自行处理则返回false。_
        } else {
          if (clickType === ClickType.SELECTED) {
      _      // 应用做自己的业务处理。_
            if (uri) {
              this.copyFile2Sandbox(uri)
                .then((result: boolean) => {
                  try {
                    this.getUIContext().getPromptAction().showToast({ message: result ? '文件拷贝成功' : '文件拷贝失败' });
                  } catch (err) {
                    console.error(\`Failed to invoke showToast\`);
                  }
                });
            }
            return true; _// 返回true则勾选，否则则不响应勾选。_
          }
          return true;
        }
      }
      async copyFile2Sandbox(filePathString: string): Promise<boolean> {
        let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
        let resFile: fileIo.File | undefined;
        try {
          resFile = fileIo.openSync(filePathString, fileIo.OpenMode.READ\_ONLY);
         _ // 创建临时文件目录_
          let dateStr = (new Date().getTime()).toString();
          let newPath = context.cacheDir + \`/${dateStr + resFile.name}\`;
         _ // 拷贝系统图库图片到沙箱_
          fileIo.copyFileSync(resFile.fd, newPath);
          return true;
        } catch (error) {
          console.error(\`Failed to copy file: ${JSON.stringify(error)}\`);
          return false;
        } finally {
          if (resFile) {
            fileIo.closeSync(resFile);
          }
        }
      }
    
    
    
    
      build() {
        Flex({
          direction: FlexDirection.Column,
          alignItems: ItemAlign.Center,
          justifyContent: FlexAlign.Center,
        }) {
          Column() {
            PhotoPickerComponent({
              pickerOptions: this.pickerOptions,
              pickerController: this.pickerController,
              onItemClicked: (itemInfo: ItemInfo, clickType: ClickType): boolean => this.onItemClicked(itemInfo, clickType),
            });
          }
          .width('100%')
          .aspectRatio(1);
        };
      }
    }
    

#### 常见FAQ

Q：PhotoViewPicker在@ohos.file.picker中跟@ohos.file.photoAccessHelper都存在，两个包下的PhotoViewPicker有什么区别？

A：@ohos.file.picker下的PhotoViewPicker适用于图片或视频类型文件的选择与保存（该接口在后续版本不再演进），请使用@ohos.file.photoAccessHelper下的[PhotoAccessHelper中的PhotoViewPicker](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-photoviewpicker)来选择图片或视频文件。

Q：用户手动取消选中uri并点击确认和直接点击右上角关闭按钮退出选择页面，这两种场景如何区分？

A：目前PhotoViewPicker取消所有已选，“完成”这个按钮是不可点的状态，只能通过右上角的关闭按钮退出选择页面。

Q：使用PhotoViewPicker是否勾选原图，返回的图片都是同一个uri，并且图片文件的大小也一样，如何区分是否勾选原图？

A：可根据返回[PhotoSelectResult](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-class#photoselectresult)中的isOriginalPhoto区分选择后的媒体文件是否为原图，系统不会主动对图片进行压缩，仅将用户选择结果传递给调用方，由调用方自行决定如何压缩处理图片。

Q：拉起媒体库的时候是否能选择沙箱里的图片文件？

A：无法选择；应用沙箱内的文件无法直接通过系统媒体库选择器（Picker）进行访问或选择，因为[沙箱路径](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-sandbox-directory)对其他应用（包括系统应用）不可见。

Q：升级手机系统后，使用PhotoPicker时出现图库使用授权的页面后直接返回。

A：应用使用PhotoPicker前，需确保图库应用本身使用协议已经同意，才能正常使用该能力。

Q：不申请ohos.permission.READ\_IMAGEVIDEO权限和PhotoPicker可以访问图库实现自定义相册吗？

A：不可以，为了保护用户的照片和视频资产隐私安全，HarmonyOS系统制定了安全隐私规则和相应技术方案，只有克隆类和云盘类应用可以申请ohos.permission.READ\_IMAGEVIDEO权限用来访问全量媒体库，其他类型应用必须通过[PhotoViewPicker](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/photoaccesshelper-photoviewpicker)来访问用户指定的图片，或者使用[AlbumPickerComponent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ohos-file-albumpickercomponent#albumpickercomponent)组件访问公共目录中的相册列表配合[PhotoPickerComponent](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/component-guidelines-photoviewpicker)使用。

Q：通过PhotoViewPicker让用户选择图片后，应用拿到photoSelectResult.photoUris，是否仅在本次APP运行时有效？

A：通过PhotoViewPicker获取到的图片photoUris在应用卸载前一直有效。

Q：使用PhotoViewPicker时，同一张图片是否可以多次被选中，比如一张图片点击两次，那么result里包含同一张图片的路径两次？

A：[pickeroptions](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ohos-file-photopickercomponent#pickeroptions)中isRepeatSelectSupported参数支持单张图片重复选择。true表示支持。默认不支持。

Q：使用PhotoViewPicker拉起相册选择，只显示gif格式的图片让用户选择，该如何实现？

A：[MimeTypeFilter](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-class#mimetypefilter19)支持文件类型的过滤配置，通过配置gif可以实现只显示gif格式的图片让用户选择。

Q：通过PhotoViewPicker.select从相册选取图片，获取到选取图片的uri数组，使用dataSharePredicates查询时，如何查询多个项目？

A：使用[PhotoAccessHelper.PhotoKeys.URI](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-photoaccesshelper-e#photokeys)做查询条件时，仅支持使用[DataSharePredicates.equalTo](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-datasharepredicates#equalto10)的方式，[DataSharePredicates.in](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-datasharepredicates#in10)不支持，可以通过for循环的方式去获取多个图片信息。

Q：使用PhotoPicker选择图片并编辑，图片的uri不变。

A：相册图片编辑后，uri不变属于规格，图片做了编辑操作，会发送图片的update通知，具体可参考[媒体资源变更通知相关指导](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/photoaccesshelper-notify-guidelines)。

Q：PhotoViewPicker是否支持设置最小选择数量？

A：PhotoViewPicker仅支持设置最大选择数量，开发者可以自行记录用户选择的媒体文件数量，在数量不足时对用户进行弹窗提示。