HarmonyOS鸿蒙Next中文件获取:读取目录下所有文件

HarmonyOS鸿蒙Next中文件获取:读取目录下所有文件 有什么方式获取 制定路径下,某个文件夹下全部数据,

现在提示 13900001 操作不允许,如何一次性授权!!!

路径:“file://docs/storage/Users/currentUser/Sounds/CallRecord”

const context = getContext() as common.UIAbilityContext;
const permissions : Array<Permissions> = ['ohos.permission.READ_AUDIO'];
abilityAccessCtrl.createAtManager().requestPermissionsFromUser(context, permissions).then((data) => {
    console.info("请求权限成功:", data);
    // 创建FileUri对象并获取沙箱路径     file://docs/storage/Users/currentUser/Sounds/CallRecord
    let fileUriObject = new fileUri.FileUri("file://docs/storage/Users/currentUser/Download");
    let sandboxPath = fileUriObject.path; // 直接获取沙箱路径
    console.info('sandboxPath', sandboxPath)
    let listFileOption : ListFileOptions = {
        recursion: false,
        listNum: 0,
        filter: {
            // suffix: [".png", ".jpg", ".jpeg"],
            // displayName: ["*abc", "efg*"],
            // fileSizeOver: 1024
        }
    };
    fs.listFile(sandboxPath, listFileOption, (err : BusinessError, filenames : Array<string>) => {
        if (err) {
            console.error(`Failed to list file. Code: ${err.code}, message: ${err.message}`);
        } else {
            console.info(`Succeeded in listing file.`);
            for (let i = 0; i < filenames.length; i++) {
                console.info(`Succeeded in listing file, file name: ${filenames[i]}`);
            }
        }
    });
}).catch((err) => {
    console.error("请求权限失败:", err);
});

更多关于HarmonyOS鸿蒙Next中文件获取:读取目录下所有文件的实战教程也可以访问 https://www.itying.com/category-93-b0.html

11 回复

背景知识:

你好,app不能直接读取非自己应用下的目录(沙箱),否则会出现目录需要权限问题(报错:13900001 操作不允许)。你只能读取自己app下的目录。或者使用 [@ohos.file.picker (选择器)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-picker) 通过系统让用户选择需要的文件。

问题解决:

读写操作可以在自己应用路径下操作,如果需要操作非应用包下的目录需要使用 picker (选择器)

更多关于HarmonyOS鸿蒙Next中文件获取:读取目录下所有文件的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


你这个问题的核心不是“权限没弹对”,而是:

结论

ohos.permission.READ_AUDIO 不是“给你整目录遍历权”的权限。
你现在直接对

file://docs/storage/Users/currentUser/Sounds/CallRecord

file://docs/storage/Users/currentUser/Download

fs.listFile(),出现 13900001 操作不允许,本质上是因为你在直接访问用户公共目录,而不是访问应用沙箱;这类访问不能靠你现在这种“拿到路径后直接扫目录”来完成。

而且官方对 READ_AUDIO 的定义本身就是受限开放权限,允许场景是克隆、备份、同步音频文件;其它场景官方给的替代方案是 AudioViewPickerRestricted Permissions


你当前代码的问题

你这里:

let fileUriObject = new fileUri.FileUri("file://docs/storage/Users/currentUser/Download");
let sandboxPath = fileUriObject.path;
fs.listFile(sandboxPath, ...)

有两个误区:

1. 这不是“沙箱路径”

你传进去的是用户公共目录 URI,fileUriObject.path 取出来也只是对应文件路径,不代表它自动变成了“你可访问的沙箱路径”。

2. requestPermissionsFromUser(['ohos.permission.READ_AUDIO']) 不等于拿到任意目录遍历权

就算用户同意了,普通应用也不能因此任意列举公共目录里的所有文件。
官方更推荐你通过 Picker 让用户显式选择文件/文件夹,再基于返回的 URI 操作。选择用户文件


想“一次性授权”,官方正确路径是什么

方案 1:用 Picker 选文件/文件夹

官方文档明确写了:

  • 通过 FilePicker 选择文件或文件夹,接口本身无需申请权限
  • Picker 返回的 URI 默认是临时读写权限
  • 如果需要长期访问,要再做持久化授权

参考:


如果你是“音频文件”

优先用:

  • picker.AudioViewPicker

官方就是把它作为访问音频类用户文件的推荐方式。选择用户文件

但注意

AudioViewPicker 更适合“选音频文件”,不是专门拿来给你扫整个目录树的。


如果你是“指定某个目录下全部文件”

你真正想做的是“目录授权 + 持久化”。

这时思路应该是:

  1. DocumentViewPicker 让用户选择文件夹或授权目标目录
  2. 拿到这个目录 URI
  3. 调用 fileShare.persistPermission(...) 持久化
  4. 下次启动时再调用 fileShare.activatePermission(...)
  5. 然后基于这个已授权 URI 去访问

参考官方文档:


你说的“如何一次性授权”

如果你的意思是:

A. “弹一次后,以后都能访问”

这可以通过:

  • Picker 先拿临时授权
  • persistPermission
  • 应用启动时 activatePermission

实现“长期可访问已授权文件/目录”。
但这不是对整个文件系统的全局授权,而是对用户明确选中的文件/目录做持久化授权。授权持久化

B. “直接对 file://docs/storage/.../CallRecord 整个目录静默授权”

普通应用没有这种官方一次性全局授权方式。


关于 Sounds/CallRecord

这个目录比较特殊。

它不是普通的“下载目录”,而是通话录音目录。这类目录通常比普通音频目录更敏感。
所以即使你申请了 READ_AUDIO,也不代表一定允许你直接枚举整个 CallRecord 目录

也就是说,你这个目标路径本身就可能比普通音频文件访问更受限。


你现在应该怎么改

如果你只是要读用户选择的音频

改用 AudioViewPicker

import { picker, fileIo } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';

let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
const audioViewPicker = new picker.AudioViewPicker(context);
const options = new picker.AudioSelectOptions();

let uris = await audioViewPicker.select(options);
// uris 里是用户选中的音频 URI

这条路不依赖 READ_AUDIO 全局扫目录。选择用户文件


如果你要长期访问某个目录/文件

再加上持久化:

import { picker, fileShare } from '@kit.CoreFileKit';

let documentPicker = new picker.DocumentViewPicker(context);
let uris = await documentPicker.select(documentSelectOptions);

let policies: fileShare.PolicyInfo[] = [{
  uri: uris[0],
  operationMode: fileShare.OperationMode.READ_MODE
}];

await fileShare.persistPermission(policies);

下次启动:

await fileShare.activatePermission(policies);

参考:授权持久化


对你这个报错的直接判断

13900001 在你这个场景下,基本可以理解为:

  • 你访问的 URI/路径没有被 Picker 授权
  • 或你在尝试直接扫公共目录
  • 或你试图对没有临时授权的 URI 做持久化/访问

官方在持久化授权的错误处理示例里,也专门提到了 13900001 这类“操作不允许”的权限/策略错误。授权持久化


最后给你一句最实用的建议

如果你的目标真的是:

“读取 CallRecord 目录下全部录音文件,并且只授权一次,以后自动读取”

那我建议你优先评估两件事:

  1. 业务上能不能改成“用户通过 Picker 选目录/选文件后,做持久化授权”
  2. 如果一定要直接扫 CallRecord,那这个需求对普通应用来说大概率不属于公开能力的推荐路径
  1. 如果是为了获取通话录音列表:放弃直接文件遍历。使用 MediaLibrary 的 getAssets(MediaType.AUDIO)。如果 MediaLibrary 也看不到通话录音,说明系统未向第三方应用开放此接口(常见于华为/荣耀等厂商的系统保护)。此时,你只能引导用户通过系统分享功能将录音发送给你的应用。
  2. 如果是为了获取应用自己生成的录音:保存到 context.getExternalFilesDir() 或context.filesDir。直接使用 fs.listFile 遍历这些沙箱目录,无需任何权限。
  3. 如果必须访问公共下载目录:使用 @kit.FileManagementKit (API 11+) 的 fileManagement.getPublicPath() 方法获取标准公共路径。确保在 module.json5 中声明了 ohos.permission.WRITE_MEDIA 或 READ_MEDIA。

权限不足啊,ohos.permission.READ_AUDIO 仅允许你通过媒体库接口访问音频元数据,而不代表你可以使用 fs.listFile 直接遍历底层物理目录。

import { picker } from '@kit.CoreFileKit';
import { fileIo as fs, ListFileOptions } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';

async function selectFolderAndListFiles() {
  try {
    const documentPicker = new picker.DocumentViewPicker();
    const documentSelectOptions = new picker.DocumentSelectOptions();
    documentPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
      // 拿到用户选择的路径 URI
      let pathUri = documentSelectResult[0];
      
      //直接对公共路径进行 listFile 仍可能受限,建议通过已授权的 URI 打开目录
      let file = fs.openSync(pathUri, fs.OpenMode.READ_ONLY);
      let listFileOption: ListFileOptions = {
        recursion: false,
        listNum: 0
      };
      
      // 使用 fd 或 uri 相关的接口读取
      fs.listFile(file.fd, listFileOption).then((filenames) => {
        filenames.forEach(name => console.info("文件名: " + name));
      });
      
    });
  } catch (err) {
    let error = err as BusinessError;
    console.error(`Picker failed. Code: ${error.code}, message: ${error.message}`);
  }
}

file://docs/storage/Users/currentUser/Sounds/CallRecord 这个路径不是你的应用沙箱路径,而是设备上用户文件的路径。

在 HarmonyOS 里,应用无法直接访问其他目录,必须用户主动授权。

ohos.permission.READ_AUDIO 只能读音频文件内容,不等于可以读目录。两个是不同权限。

建议通过 FilePicker 让用户"主动选择要授权的文件夹",授权后你的应用才能访问。

不对吧,你要读的路径:file://docs/storage/Users/currentUser/Sounds/CallRecord

这个是系统公共目录,不是沙箱啊!

你只申请 READ_AUDIO 不够,会直接报:13900001 Operation not allowed

必须一次性授权 3 个权限

ohos.permission.READ_AUDIO
ohos.permission.WRITE_AUDIO
ohos.permission.FILE_ACCESS_PUBLIC

读取路径是这个:

import { Environment } from '@kit.EnvironmentKit';

// 获取系统公共的 Sounds/CallRecord 目录
const publicSoundDir = Environment.PublicDirectory.DIRECTORY_SOUNDS + "/CallRecord";

应用只能访问沙箱内的文件,公共目录下的文件需要用户授权,

这个路径看命名属于公共目录,怎么能拉起授权

公共目录下的文件/目录(除了“Download/包名/“目录)没法直接访问

在HarmonyOS Next中,读取目录下所有文件可使用 @ohos.file.fs 模块的 listDir() 方法。例如:fs.listDir("/data/storage/el2/base/haps/entry/files/") 返回 FileInfo[],包含文件名、大小等信息。同步版本 listDirSync() 也可用。需在 module.json5 中声明 ohos.permission.READ_MEDIA 权限(或对应存储权限)。路径需根据应用沙箱调整。

在HarmonyOS Next中,13900001(操作不允许)是因为fs.listFile不能直接读取像Sounds/CallRecord这样的公共媒体目录。虽然申请了ohos.permission.READ_AUDIO,但该权限只允许通过系统媒体库接口(如@ohos.multimedia.mediaLibrary)访问音频文件,并不赋予文件系统直接列表目录的能力。要读取录音文件列表,应使用audioAccessHelper等专门的媒体访问API,通过查询媒体存储获取文件,而非原生文件路径。公共目录无法“一次性授权”给fs接口;只有应用沙箱目录(如context.filesDir)才可以通过fs.listFile自由读列。如需批量处理,可将文件提前存入沙箱。

回到顶部