HarmonyOS 鸿蒙Next应用如何获取用户设备内文件?

HarmonyOS 鸿蒙Next应用如何获取用户设备内文件?

鸿蒙应用如何获取用户设备内文件?

4 回复

一、结论

应用访问用户设备内的文件(图片,视频,音频,文档文件,xx文件)等用户文件时,我们可以通过系统预置的文件选择器(FilePicker),实现该能力。

通过Picker访问相关文件,将拉起对应的应用,引导用户完成界面操作,接口本身无需申请权限。

但是picker获取的uri只具有临时权限,应用后台关闭后就失去了访问权限。

二、代码实现和详细解释

用户相音频文件选择:

使用系统提供的AudioViewPicker来访问用户的音频资源,当应用选择成功,点击确认后,会通过回调得到文件资源的Uri。

通过文件资源的Uri。进行FS操作,来读写用户音频文件。

import { picker } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

/**
 * 访问器管理工具
 */
export class PickerMgr {
  private TAG: string = 'PickerMgr';

  private static mPickerMgr: PickerMgr | null = null;

  public static Ins(): PickerMgr {
    if (!PickerMgr.mPickerMgr) {
      PickerMgr.mPickerMgr = new PickerMgr();
    }
    return PickerMgr.mPickerMgr;
  }

  /**
   * 选择用户设备内的所有音频资源
   */
  public selectAudio(context: common.Context, selectNum: number): Promise<Array<string>>{
    return new Promise((resolve, reject)=>{
      // 创建音频选择器
      const audioViewPicker = new picker.AudioViewPicker(context);
      // 设置音频选择操作参数
      const audioSelectOptions = new picker.AudioSelectOptions();
      audioSelectOptions.maxSelectNumber = selectNum;
      // 向用户唤起系统选择界面
      audioViewPicker.select(audioSelectOptions).then((audioSelectResult: Array<string>) => {
        // 文件选择成功后,返回被选中音频的uri结果集。
        console.info('audioViewPicker.select to file succeed and uri is:' + JSON.stringify(audioSelectResult));
        resolve(audioSelectResult);
      }).catch((err: BusinessError) => {
        // 异常返回
        console.error(`Invoke audioViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
        reject(err);
      })
    });
  }
}
import { PickerMgr } from '../../mgr/PickerMgr'
import { common } from '@kit.AbilityKit';
import { fileIo as FS } from '@kit.CoreFileKit';

/**
 * 用户音频选择
 */
@Entry
@Component
@Preview
struct AudioSelectPage {

  /**
   * 点击唤起音频选择器
   */
  onSelectUserAudioFile = async ()=>{
    // 请确保 getContext(this) 返回结果为 UIAbilityContext
    let context = getContext(this) as common.Context;
    let res = await PickerMgr.Ins().selectAudio(context, 1);
    this.optionAudioFile(res);
  }

  /**
   * 对于音频文件进行文件读写操作
   * @param uri
   */
  private optionAudioFile(uri: string){
    // 这里需要注意接口权限参数是 fs.OpenMode.READ_ONLY。只读.
    let file = FS.openSync(uri, FS.OpenMode.READ_ONLY);
    console.info('file fd: ' + file.fd);
    // 读取到系统文件标识符后,就可以针对这个文件进行操作。可以理解为文件在HarmonyOS系统中的ID。
    let buffer = new ArrayBuffer(4096);
    let readLen = FS.readSync(file.fd, buffer);
    console.info('readSync data to file succeed and buffer size is:' + readLen);
    //读取完成后关闭fd。
    FS.closeSync(file);
  }

  build() {
    RelativeContainer() {
      Text("点击唤起音频选择器")
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(this.onSelectUserAudioFile)
    }
    .height('100%')
    .width('100%')
  }
}

使用picker获取的select()返回的uri权限是临时只读权限,待退出应用后台后,获取的临时权限就会失效。 使用picker获取的select()返回的uri权限是临时只读权限,待退出应用后台后,获取的临时权限就会失效。 使用picker获取的select()返回的uri权限是临时只读权限,待退出应用后台后,获取的临时权限就会失效。

用户图片和视频相册选择:

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

/**
 * 相册图片选择
 */
@Entry
@Component
struct AlbumSelectPage {

  private TAG: string = "AlbumSelectPage";

  onClickSelectPhoto = ()=>{
    try {
      let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
      // 设置筛选过滤条件
      PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
      // 选择用户选择数量
      PhotoSelectOptions.maxSelectNumber = 1;
      // 实例化图片选择器
      let photoPicker = new photoAccessHelper.PhotoViewPicker();
      // 唤起安全相册组件
      photoPicker.select(PhotoSelectOptions, (err: BusinessError, PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {
        if (err) {
          console.error(this.TAG, "onClickSelectPhoto photoPicker.select error:" + JSON.stringify(err));
          return;
        }
        // 用户选择确认后,会回调到这里。
        console.info(this.TAG, "onClickSelectPhoto photoPicker.select successfully:" + JSON.stringify(PhotoSelectResult));
      });
    } catch (error) {
      let err: BusinessError = error as BusinessError;
      console.error(this.TAG, "onClickSelectPhoto photoPicker.select catch failed:" + JSON.stringify(err));
    }
  }

  build() {
    Row(){
      Button('点击唤起相册选择')
        .onClick(this.onClickSelectPhoto)
    }
    .justifyContent(FlexAlign.Center)
    .size({
      width: "100%",
      height: "100%"
    })
  }
}

文件选择示例:

import { picker } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

/**
 * 文件选择
 */
@Entry
@Component
struct FileSelectPage {

  build() {
    RelativeContainer() {
      Text("点击唤起文件选择器")
        .fontSize(px2fp(42))
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(()=>{
          const documentSelectOptions = new picker.DocumentSelectOptions();
          // 选择文档的最大数目(可选)
          documentSelectOptions.maxSelectNumber = 5;
          // 指定选择的文件或者目录路径(可选)
          documentSelectOptions.defaultFilePathUri = "file://docs/storage/Users/currentUser/test";
          // 选择文件的后缀类型['后缀类型描述|后缀类型'](可选) 若选择项存在多个后缀名,则每一个后缀名之间用英文逗号进行分隔(可选),后缀类型名不能超过100,选择所有文件:'所有文件(*.*)|.*';
          documentSelectOptions.fileSuffixFilters = ['图片(.png, .jpg)|.png,.jpg', '文档|.txt', '视频|.mp4', '.pdf'];
          //选择是否对指定文件或目录授权,true为授权,当为true时,defaultFilePathUri为必选参数,拉起文管授权界面;false为非授权,拉起常规文管界面(可选)
          documentSelectOptions.authMode = true;

          let uris: Array<string> = [];
          let context = getContext(this) as common.Context; // 请确保 getContext(this) 返回结果为 UIAbilityContext
          // 创建文件选择器实例
          const documentViewPicker = new picker.DocumentViewPicker(context);
          documentViewPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
            //文件选择成功后,返回被选中文档的uri结果集。
            uris = documentSelectResult;
            console.info('documentViewPicker.select to file succeed and uris are:' + uris);
          }).catch((err: BusinessError) => {
            console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
          })
          
        })
    }
    .height('100%')
    .width('100%')
  }
}

更多关于HarmonyOS 鸿蒙Next应用如何获取用户设备内文件?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


通过 Picker

在HarmonyOS Next中,应用通过FileManager和FileAccessKit框架获取设备内文件。首先需在module.json5中声明ohos.permission.READ_MEDIA权限。使用FileAccessHelper创建实例,通过openFile选择文件或getRoots获取沙箱外目录。通过uri访问文件元数据和内容。

在HarmonyOS Next中,应用获取用户设备内的文件主要通过文件管理接口用户授权来实现。核心步骤如下:

  1. 声明权限:在应用的module.json5配置文件中,声明必要的文件访问权限,例如ohos.permission.READ_MEDIA(读取媒体文件)或ohos.permission.WRITE_MEDIA(写入媒体文件)。

  2. 动态申请权限:在运行时通过abilityAccessCtrl接口动态向用户申请权限,确保用户知情并授权。

  3. 使用文件选择器:推荐使用系统提供的文件选择器(FilePicker),让用户主动选择需要访问的文件。这种方式遵循最小权限原则,无需申请广泛的存储权限。例如:

    • 使用PhotoViewPicker选择图片/视频。
    • 使用DocumentViewPicker选择文档。
    • 使用AudioViewPicker选择音频文件。
  4. 访问公共目录:对于媒体类文件(图片、视频、音频),可以通过mediaLibrary接口访问公共媒体库,但需要用户授权。

  5. 安全沙箱访问:应用自身的沙箱目录(context.filesDir)可直接读写,无需权限。

关键注意事项

  • HarmonyOS Next强化了隐私安全,禁止随意访问设备存储根目录。
  • 优先使用文件选择器,而非直接请求存储权限。
  • 遵循“一次授权、单次访问”原则,确保用户对文件访问有完全控制。

示例代码(使用文件选择器选择图片):

import { picker } from '@kit.FileKit';

let photoPicker = new picker.PhotoViewPicker();
photoPicker.select()
  .then((photoSelectResult) => {
    // 处理用户选择的文件
  })
  .catch((err) => {
    console.error('文件选择失败');
  });

通过以上方式,可在保障用户隐私的前提下,安全获取设备文件。

回到顶部