OpenHarmony 公共目录访问 Demo 技术文档
1. 文档目标
本文档用于说明当前 DirAccess 项目在 OpenHarmony 5.0.1 镜像上的公共目录访问实现方案,重点覆盖:
- 项目适用范围与前提条件
- 项目级权限配置
- 核心代码接口说明
Desktop/Documents/Download与Photo/Videos/Audio的差异化实现- 系统应用与普通应用的能力边界
- 常见问题与排障建议
2. 适用范围与前提
- 系统版本:OpenHarmony 5.0.1(当前工程配置使用 API 14)
- 工程类型:Stage 模型 ArkTS 应用
- 核心前提 :本 Demo 设计为系统应用场景
Desktop/Documents/Download自动枚举依赖系统能力(fileAccess相关)- 应用需具备系统签名/系统授权能力
说明:普通三方应用在本方案下通常无法稳定实现
Desktop/Documents/Download的无交互全量枚举。
3. 项目结构(与本能力相关)
- 页面入口:
entry/src/main/ets/pages/Index.ets - 目录工具:
entry/src/main/ets/utils/CommonDirectoryUtil.ets - 模块权限:
entry/src/main/module.json5 - 编译配置:
build-profile.json5
4. 项目级配置说明
4.1 OpenHarmony 构建目标
build-profile.json5 中关键配置:
runtimeOS:OpenHarmonycompileSdkVersion:14compatibleSdkVersion:14targetSdkVersion:14
这保证工程按 OpenHarmony API 能力编译与运行。
4.2 模块权限配置(entry/src/main/module.json5)
当前实现使用两类权限:
A. 系统权限(system_grant,系统应用能力)
ohos.permission.FILE_ACCESS_MANAGERohos.permission.GET_BUNDLE_INFO_PRIVILEGED
用途:支持 fileAccess 对文档类公共目录 URI 的自动访问与枚举能力。
B. 用户授权权限(user_grant)
ohos.permission.READ_WRITE_DESKTOP_DIRECTORYohos.permission.READ_WRITE_DOCUMENTS_DIRECTORYohos.permission.READ_WRITE_DOWNLOAD_DIRECTORYohos.permission.READ_IMAGEVIDEOohos.permission.WRITE_IMAGEVIDEOohos.permission.READ_AUDIOohos.permission.WRITE_AUDIOohos.permission.READ_MEDIA
注意事项:
- user_grant 权限在
module.json5中必须配置reason与usedScene - 系统权限不走普通用户弹窗授权流程,由系统应用能力与签名体系决定是否生效
C. module.json5
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"default",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"requestPermissions": [
{
"name": "ohos.permission.FILE_ACCESS_MANAGER"
},
{
"name": "ohos.permission.GET_BUNDLE_INFO_PRIVILEGED"
},
{
"name": "ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY",
"reason": "$string:perm_reason_documents",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY",
"reason": "$string:perm_reason_download",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.READ_WRITE_DESKTOP_DIRECTORY",
"reason": "$string:perm_reason_desktop",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.READ_IMAGEVIDEO",
"reason": "$string:perm_reason_image_video",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.WRITE_IMAGEVIDEO",
"reason": "$string:perm_reason_image_video",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.READ_AUDIO",
"reason": "$string:perm_reason_audio",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.WRITE_AUDIO",
"reason": "$string:perm_reason_audio",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.READ_MEDIA",
"reason": "$string:perm_reason_media",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
],
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"extensionAbilities": [
{
"name": "EntryBackupAbility",
"srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
"type": "backup",
"exported": false,
"metadata": [
{
"name": "ohos.extension.backup",
"resource": "$profile:backup_config"
}
]
}
]
}
}
5. 页面行为说明(Index.ets)
页面提供 6 个按钮:
DesktopDocumentsDownloadPhotoVideosAudio
点击按钮后的统一流程:
- 调用
CommonDirectoryUtil.ensureAllPermissions(context)检查/申请权限 - 调用
CommonDirectoryUtil.listDirectoryEntries(context, dirName)获取目录内容 - 展示解析路径、条目列表、最近调试日志
页面主要状态字段:
currentDirName: 当前目录名称currentPath: 当前解析到的路径或标识status: 当前操作状态(成功/失败)items: 目录项名称数组debugLogs: 调试日志(来自工具类)
6. 核心工具类接口说明(CommonDirectoryUtil.ets)
6.1 公开接口
ensureAllPermissions(context: common.UIAbilityContext): Promise<boolean>
作用:
- 校验 user_grant 权限是否已授予
- 对未授予的 user_grant 权限触发
requestPermissionsFromUser - 校验系统权限状态并写入日志(不进行用户弹窗申请)
返回:
true: 当前必要权限满足false: 存在权限缺失或授权失败
listDirectoryEntries(context: common.UIAbilityContext, dirName: string): Promise<DirListResult>
作用:按目录类型路由到不同实现。
Desktop/Documents/Download- 优先走
fileAccessURI 自动枚举(系统应用方案) - 失败时回退到
fs + 环境路径/候选路径探测
- 优先走
Photo/Videos/Audio- 走
mediaLibrary查询媒体资产并返回displayName列表
- 走
返回结构 DirListResult:
resolvedPath: string- 成功时:命中的目录路径或 URI
- 失败时:空字符串
items: string[]- 目录项名称列表(或媒体名称列表)
getDebugLogs(): string[]
作用:返回最近缓存的调试日志,供页面展示与问题排查。
resetCache(): void
作用:清空各目录缓存路径,强制下次重新探测。
6.2 内部实现策略(关键点)
A. 文档类目录(Desktop/Documents/Download)
优先策略(系统应用) :fileAccess URI 访问
- Desktop:
file://docs/storage/Users/currentUser/Desktop - Documents:
file://docs/storage/Users/currentUser/Documents - Download:
file://docs/storage/Users/currentUser/Download(含Downloads兜底)
执行链路:
fileAccess.createFileAccessHelper(context)getFileInfoFromUri(uri)listFile()+ 迭代器next()收集fileName
回退策略(兼容) :environment + fs 路径探测
- 先尝试
environment.getUserDesktopDir/getUserDocumentDir/getUserDownloadDir - 再尝试预置路径候选并用
fs.listFileSync/statSync/accessSync验证
B. 媒体类目录(Photo/Videos/Audio)
通过 mediaLibrary 查询对应 MediaType:
- Photo ->
IMAGE - Videos ->
VIDEO - Audio ->
AUDIO
再提取媒体资产名称列表并返回。
7. 系统应用 vs 普通应用能力边界
7.1 当前方案结论
- 系统应用:可按当前 Demo 思路自动枚举 6 类目录(文档类依赖系统能力)
- 普通应用 :
- 通常仅能稳定处理
Photo/Videos/Audio(媒体库能力) Desktop/Documents/Download的"无交互自动枚举"不可靠或不可用
- 通常仅能稳定处理
7.2 为什么普通应用受限
文档类目录自动枚举在不同设备镜像上受以下因素影响:
- 环境能力是否开放(
environment可能返回能力不支持) - 目录挂载是否对普通应用可见
fileAccess相关能力是否需要系统权限
因此普通应用一般需要采用"用户选择授权(Picker)"模式,而非固定路径自动扫描。
8. 日志与排障建议
工具类日志统一前缀:[CommonDirectoryUtil],建议重点关注:
system permission ... : 0/-1
判断系统权限是否生效requesting permissions、authResults
判断 user_grant 权限是否通过start fileAccess listing .../fileAccess success/fileAccess failed
判断文档类目录系统链路是否可用environment path resolve failed ...
判断环境能力是否支持all candidates failed for dir=...
判断回退路径是否全部不可达
排障顺序建议:
- 先看系统权限状态日志是否为
PERMISSION_GRANTED(0) - 再看
fileAccess是否命中 URI - 再看 environment/路径候选是否均失败
- 最后确认镜像版本、系统应用签名、权限预授权策略
9. 二次开发建议
- 若目标是产品化系统工具:保留
fileAccess主链路,增加更多 URI 候选与更细粒度错误码映射 - 若目标是三方应用上架:建议将文档类目录切换为用户选择授权模式,不建议继续硬编码路径探测
- 建议将媒体能力逐步迁移到更新 API(减少 deprecated 告警)
10. 快速使用说明(开发者)
- 确认工程为 OpenHarmony 构建目标
- 确认
module.json5已包含本文档列出的权限 - 确认应用具备系统应用能力(用于文档类目录自动枚举)
- 启动应用,点击 6 个目录按钮
- 通过页面底部"调试日志(最近)"确认实际访问链路与结果
11. Index.ets
import common from '@ohos.app.ability.common';
import { CommonDirectoryUtil } from '../utils/CommonDirectoryUtil';
@Entry
@Component
struct Index {
@State currentDirName: string = '未选择目录';
@State currentPath: string = '';
@State status: string = '请点击按钮查看目录内容';
@State items: string[] = [];
@State debugLogs: string[] = [];
private async showDirectory(dirName: string): Promise<void> {
const context = getContext(this) as common.UIAbilityContext;
const permissionGranted = await CommonDirectoryUtil.ensureAllPermissions(context);
if (!permissionGranted) {
this.status = '权限未全部授予,无法读取目录';
this.currentDirName = dirName;
this.currentPath = '';
this.items = [];
this.debugLogs = CommonDirectoryUtil.getDebugLogs();
return;
}
const result = await CommonDirectoryUtil.listDirectoryEntries(context, dirName);
this.currentDirName = dirName;
this.currentPath = result.resolvedPath;
this.debugLogs = CommonDirectoryUtil.getDebugLogs();
if (!result.resolvedPath) {
this.status = '未解析到目录路径(请确认设备支持并已授权)';
this.items = [];
return;
}
this.items = result.items;
this.status = '共 ' + result.items.length + ' 项';
}
@Builder
private actionButton(name: string) {
Button(name)
.fontSize(15)
.width('30%')
.height(44)
.onClick(() => {
this.showDirectory(name);
});
}
build() {
Column({ space: 10 }) {
Text('公共目录浏览')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 8, bottom: 4 });
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
this.actionButton('Desktop');
this.actionButton('Documents');
this.actionButton('Download');
this.actionButton('Photo');
this.actionButton('Videos');
this.actionButton('Audio');
}
.width('100%')
.margin({ bottom: 8 });
Text('当前目录: ' + this.currentDirName)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.width('100%');
Text('路径: ' + (this.currentPath || '(空)'))
.fontSize(13)
.fontColor('#666666')
.maxLines(3)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width('100%');
Text(this.status)
.fontSize(14)
.fontColor('#0A59F7')
.width('100%')
.margin({ top: 2, bottom: 4 });
List({ space: 4 }) {
ForEach(this.items, (item: string) => {
ListItem() {
Text(item)
.fontSize(14)
.width('100%')
.padding({
top: 8,
bottom: 8,
left: 10,
right: 10
})
.backgroundColor('#F5F7FA')
.borderRadius(6);
}
}, (item: string) => item);
}
.width('100%')
.layoutWeight(1)
.margin({ bottom: 8 });
Text('调试日志(最近)')
.fontSize(14)
.fontColor('#666666')
.width('100%');
List({ space: 2 }) {
ForEach(this.debugLogs, (item: string) => {
ListItem() {
Text(item)
.fontSize(11)
.width('100%')
.fontColor('#999999')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis });
}
}, (item: string, index: number) => item + index);
}
.width('100%')
.height(120);
}
.padding(12)
.width('100%')
.height('100%');
}
}
12. CommonDirectoryUtil.ets
import fs from '@ohos.file.fs';
import environment from '@ohos.file.environment';
import fileAccess from '@ohos.file.fileAccess';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import type { Permissions, PermissionRequestResult } from '@ohos.abilityAccessCtrl';
import common from '@ohos.app.ability.common';
import mediaLibrary from '@ohos.multimedia.mediaLibrary';
const TAG = '[CommonDirectoryUtil]';
const DOCUMENT_PATH_CANDIDATES: string[] = [
'/storage/Users/currentUser/Documents',
'/storage/Users/currentUser/Document',
'/storage/media/100/local/files/Docs/Documents',
'/data/service/el2/100/hmdfs/account/files/Docs/Documents'
];
const DOWNLOAD_PATH_CANDIDATES: string[] = [
'/storage/Users/currentUser/Download',
'/storage/Users/currentUser/Downloads',
'/storage/media/100/local/files/Docs/Download',
'/data/service/el2/100/hmdfs/account/files/Docs/Download'
];
const DESKTOP_PATH_CANDIDATES: string[] = [
'/storage/Users/currentUser/Desktop',
'/storage/media/100/local/files/Docs/Desktop',
'/data/service/el2/100/hmdfs/account/files/Docs/Desktop'
];
const PHOTO_PATH_CANDIDATES: string[] = [
'/storage/Users/Photo',
'/storage/Users/currentUser/Photo',
'/storage/Users/currentUser/Pictures',
'/storage/media/100/local/files/Photo',
'/data/service/el2/100/hmdfs/account/files/Photo'
];
const VIDEOS_PATH_CANDIDATES: string[] = [
'/storage/Users/Videos',
'/storage/Users/currentUser/Videos',
'/storage/media/100/local/files/Videos',
'/data/service/el2/100/hmdfs/account/files/Videos'
];
const AUDIO_PATH_CANDIDATES: string[] = [
'/storage/Users/Audio',
'/storage/Users/currentUser/Audio',
'/storage/Users/currentUser/Music',
'/storage/media/100/local/files/Audio',
'/data/service/el2/100/hmdfs/account/files/Audio'
];
const DESKTOP_URI_CANDIDATES: string[] = [
'file://docs/storage/Users/currentUser/Desktop'
];
const DOCUMENT_URI_CANDIDATES: string[] = [
'file://docs/storage/Users/currentUser/Documents'
];
const DOWNLOAD_URI_CANDIDATES: string[] = [
'file://docs/storage/Users/currentUser/Download',
'file://docs/storage/Users/currentUser/Downloads'
];
const USER_GRANT_PERMISSIONS: Permissions[] = [
'ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY' as Permissions,
'ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY' as Permissions,
'ohos.permission.READ_WRITE_DESKTOP_DIRECTORY' as Permissions,
'ohos.permission.READ_IMAGEVIDEO' as Permissions,
'ohos.permission.WRITE_IMAGEVIDEO' as Permissions,
'ohos.permission.READ_AUDIO' as Permissions,
'ohos.permission.WRITE_AUDIO' as Permissions,
'ohos.permission.READ_MEDIA' as Permissions
];
const SYSTEM_PERMISSIONS: Permissions[] = [
'ohos.permission.FILE_ACCESS_MANAGER' as Permissions,
'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED' as Permissions
];
export class CommonDirectoryUtil {
private static documentsPath: string = '';
private static downloadsPath: string = '';
private static desktopPath: string = '';
private static photoPath: string = '';
private static videosPath: string = '';
private static audioPath: string = '';
static readonly DEBUG_LOGS: string[] = [];
static resetCache(): void {
CommonDirectoryUtil.documentsPath = '';
CommonDirectoryUtil.downloadsPath = '';
CommonDirectoryUtil.desktopPath = '';
CommonDirectoryUtil.photoPath = '';
CommonDirectoryUtil.videosPath = '';
CommonDirectoryUtil.audioPath = '';
}
private static log(message: string): void {
const line = `${TAG} ${message}`;
console.info(line);
CommonDirectoryUtil.DEBUG_LOGS.push(line);
if (CommonDirectoryUtil.DEBUG_LOGS.length > 80) {
CommonDirectoryUtil.DEBUG_LOGS.shift();
}
}
static getDebugLogs(): string[] {
return [...CommonDirectoryUtil.DEBUG_LOGS];
}
private static getCandidatesByDirName(dirName: string): string[] {
if (dirName === 'Desktop') {
return DESKTOP_PATH_CANDIDATES;
}
if (dirName === 'Documents') {
return DOCUMENT_PATH_CANDIDATES;
}
if (dirName === 'Download') {
return DOWNLOAD_PATH_CANDIDATES;
}
if (dirName === 'Photo') {
return PHOTO_PATH_CANDIDATES;
}
if (dirName === 'Videos') {
return VIDEOS_PATH_CANDIDATES;
}
if (dirName === 'Audio') {
return AUDIO_PATH_CANDIDATES;
}
return [];
}
private static setCachedPath(dirName: string, path: string): void {
if (dirName === 'Desktop') {
CommonDirectoryUtil.desktopPath = path;
} else if (dirName === 'Documents') {
CommonDirectoryUtil.documentsPath = path;
} else if (dirName === 'Download') {
CommonDirectoryUtil.downloadsPath = path;
} else if (dirName === 'Photo') {
CommonDirectoryUtil.photoPath = path;
} else if (dirName === 'Videos') {
CommonDirectoryUtil.videosPath = path;
} else if (dirName === 'Audio') {
CommonDirectoryUtil.audioPath = path;
}
}
private static getCachedPath(dirName: string): string {
if (dirName === 'Desktop') {
return CommonDirectoryUtil.desktopPath;
}
if (dirName === 'Documents') {
return CommonDirectoryUtil.documentsPath;
}
if (dirName === 'Download') {
return CommonDirectoryUtil.downloadsPath;
}
if (dirName === 'Photo') {
return CommonDirectoryUtil.photoPath;
}
if (dirName === 'Videos') {
return CommonDirectoryUtil.videosPath;
}
if (dirName === 'Audio') {
return CommonDirectoryUtil.audioPath;
}
return '';
}
static async ensureAllPermissions(context: common.UIAbilityContext): Promise<boolean> {
try {
const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
const tokenId: number = context.applicationInfo.accessTokenId;
const needRequest: Permissions[] = [];
for (let i = 0; i < USER_GRANT_PERMISSIONS.length; i++) {
const perm = USER_GRANT_PERMISSIONS[i];
try {
const status = atManager.checkAccessTokenSync(tokenId, perm);
if (status !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
needRequest.push(perm);
}
} catch (_) {
needRequest.push(perm);
}
}
for (let i = 0; i < SYSTEM_PERMISSIONS.length; i++) {
const perm = SYSTEM_PERMISSIONS[i];
try {
const status = atManager.checkAccessTokenSync(tokenId, perm);
CommonDirectoryUtil.log(`system permission ${perm}: ${status}`);
} catch (e) {
CommonDirectoryUtil.log(`system permission check failed ${perm}: ${JSON.stringify(e)}`);
}
}
if (needRequest.length === 0) {
CommonDirectoryUtil.log('all permissions already granted');
return true;
}
CommonDirectoryUtil.log(`requesting permissions: ${JSON.stringify(needRequest)}`);
const allGranted = await new Promise<boolean>((resolve) => {
atManager.requestPermissionsFromUser(context, needRequest, (err, data: PermissionRequestResult) => {
if (err) {
CommonDirectoryUtil.log(`requestPermissionsFromUser error: ${JSON.stringify(err)}`);
resolve(false);
return;
}
if (!data || !data.authResults) {
CommonDirectoryUtil.log('requestPermissionsFromUser returned empty result');
resolve(false);
return;
}
CommonDirectoryUtil.log(`permission authResults: ${JSON.stringify(data.authResults)}`);
for (let i = 0; i < data.authResults.length; i++) {
if (data.authResults[i] !== 0) {
resolve(false);
return;
}
}
resolve(true);
});
});
CommonDirectoryUtil.resetCache();
return allGranted;
} catch (_) {
CommonDirectoryUtil.log('ensureAllPermissions exception');
return false;
}
}
static async listDirectoryEntries(context: common.UIAbilityContext, dirName: string): Promise<DirListResult> {
if (dirName === 'Desktop' || dirName === 'Documents' || dirName === 'Download') {
const byFileAccess = await CommonDirectoryUtil.listByFileAccess(context, dirName);
if (byFileAccess.resolvedPath) {
return byFileAccess;
}
CommonDirectoryUtil.log(`fileAccess fallback to fs path probing for ${dirName}`);
}
if (dirName === 'Photo') {
return await CommonDirectoryUtil.listMediaByType(context, mediaLibrary.MediaType.IMAGE, 'Photo');
}
if (dirName === 'Videos') {
return await CommonDirectoryUtil.listMediaByType(context, mediaLibrary.MediaType.VIDEO, 'Videos');
}
if (dirName === 'Audio') {
return await CommonDirectoryUtil.listMediaByType(context, mediaLibrary.MediaType.AUDIO, 'Audio');
}
const cached = CommonDirectoryUtil.getCachedPath(dirName);
const candidates = cached ? [cached, ...CommonDirectoryUtil.getCandidatesByDirName(dirName)] : CommonDirectoryUtil.getCandidatesByDirName(dirName);
let uniqueCandidates = candidates.filter((item, idx) => candidates.indexOf(item) === idx);
uniqueCandidates = CommonDirectoryUtil.prependEnvironmentPath(dirName, uniqueCandidates);
CommonDirectoryUtil.log(`start listDirectoryEntries dir=${dirName}`);
CommonDirectoryUtil.log(`candidates=${JSON.stringify(uniqueCandidates)}`);
for (let i = 0; i < uniqueCandidates.length; i++) {
const candidate = uniqueCandidates[i];
try {
const names = fs.listFileSync(candidate);
CommonDirectoryUtil.setCachedPath(dirName, candidate);
CommonDirectoryUtil.log(`candidate success: ${candidate}, count=${names.length}`);
const result = new DirListResult();
result.resolvedPath = candidate;
result.items = names;
return result;
} catch (e) {
CommonDirectoryUtil.log(`listFileSync failed: ${candidate}, error=${JSON.stringify(e)}`);
}
try {
const stat = fs.statSync(candidate);
CommonDirectoryUtil.log(`statSync: ${candidate}, isDir=${stat.isDirectory()}`);
} catch (e) {
CommonDirectoryUtil.log(`statSync failed: ${candidate}, error=${JSON.stringify(e)}`);
}
try {
const accessResult = fs.accessSync(candidate);
CommonDirectoryUtil.log(`accessSync: ${candidate}, result=${accessResult}`);
} catch (e) {
CommonDirectoryUtil.log(`accessSync failed: ${candidate}, error=${JSON.stringify(e)}`);
}
}
CommonDirectoryUtil.log(`all candidates failed for dir=${dirName}`);
const failed = new DirListResult();
return failed;
}
private static getUriCandidatesByDirName(dirName: string): string[] {
if (dirName === 'Desktop') {
return DESKTOP_URI_CANDIDATES;
}
if (dirName === 'Documents') {
return DOCUMENT_URI_CANDIDATES;
}
if (dirName === 'Download') {
return DOWNLOAD_URI_CANDIDATES;
}
return [];
}
private static async listByFileAccess(context: common.UIAbilityContext, dirName: string): Promise<DirListResult> {
const result = new DirListResult();
const candidates = CommonDirectoryUtil.getUriCandidatesByDirName(dirName);
CommonDirectoryUtil.log(`start fileAccess listing dir=${dirName}, uriCandidates=${JSON.stringify(candidates)}`);
try {
const helper = fileAccess.createFileAccessHelper(context);
for (let i = 0; i < candidates.length; i++) {
const uri = candidates[i];
try {
const fileInfo = await helper.getFileInfoFromUri(uri);
const iterator = fileInfo.listFile();
const names: string[] = [];
let done = false;
while (!done) {
const node = iterator.next();
if (node.value) {
names.push(node.value.fileName);
}
done = node.done;
}
result.resolvedPath = uri;
result.items = names;
CommonDirectoryUtil.log(`fileAccess success dir=${dirName}, uri=${uri}, count=${names.length}`);
return result;
} catch (e) {
CommonDirectoryUtil.log(`fileAccess failed uri=${uri}, error=${JSON.stringify(e)}`);
}
}
CommonDirectoryUtil.log(`fileAccess all uri candidates failed for ${dirName}`);
return result;
} catch (e) {
CommonDirectoryUtil.log(`fileAccess helper create failed for ${dirName}: ${JSON.stringify(e)}`);
return result;
}
}
private static prependEnvironmentPath(dirName: string, candidates: string[]): string[] {
let envPath = '';
try {
if (dirName === 'Desktop') {
envPath = environment.getUserDesktopDir();
} else if (dirName === 'Documents') {
envPath = environment.getUserDocumentDir();
} else if (dirName === 'Download') {
envPath = environment.getUserDownloadDir();
}
if (envPath) {
CommonDirectoryUtil.log(`environment path for ${dirName}: ${envPath}`);
}
} catch (e) {
CommonDirectoryUtil.log(`environment path resolve failed for ${dirName}: ${JSON.stringify(e)}`);
}
if (!envPath) {
return candidates;
}
const merged = [envPath, ...candidates];
return merged.filter((item, idx) => merged.indexOf(item) === idx);
}
private static async listMediaByType(context: common.UIAbilityContext, mediaType: mediaLibrary.MediaType, label: string): Promise<DirListResult> {
const result = new DirListResult();
result.resolvedPath = `MediaLibrary:${label}`;
CommonDirectoryUtil.log(`start media query type=${label}`);
try {
const media: mediaLibrary.MediaLibrary = mediaLibrary.getMediaLibrary(context);
const options: mediaLibrary.MediaFetchOptions = {
selections: `${mediaLibrary.FileKey.MEDIA_TYPE}=?`,
selectionArgs: [String(mediaType)]
};
const fetch: mediaLibrary.FetchFileResult = await media.getFileAssets(options);
const assets: mediaLibrary.FileAsset[] = await fetch.getAllObject();
const names: string[] = [];
for (let i = 0; i < assets.length; i++) {
const item = assets[i];
names.push(item.displayName);
}
fetch.close();
result.items = names;
CommonDirectoryUtil.log(`media query success type=${label}, count=${names.length}`);
return result;
} catch (e) {
CommonDirectoryUtil.log(`media query failed type=${label}, error=${JSON.stringify(e)}`);
result.resolvedPath = '';
return result;
}
}
}
class DirListResult {
resolvedPath: string = '';
items: string[] = [];
}
附1
运行截图:

附2
配置应用为系统应用
在你的sdk目录下找到如下路径:OpenHarmony\Sdk\14\toolchains\lib

修改这两个文件内容如下:
{
"version-name": "2.0.0",
"version-code": 2,
"uuid": "fe686e1b-3770-4824-a938-961b140a7c98",
"validity": {
"not-before": 1610519532,
"not-after": 1705127532
},
"type": "debug",
"bundle-info": {
"developer-id": "OpenHarmony",
"development-certificate": "-----BEGIN CERTIFICATE-----\nMIICMzCCAbegAwIBAgIEaOC/zDAMBggqhkjOPQQDAwUAMGMxCzAJBgNVBAYTAkNO\nMRQwEgYDVQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVh\nbTEjMCEGA1UEAxMaT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gQ0EwHhcNMjEwMjAy\nMTIxOTMxWhcNNDkxMjMxMTIxOTMxWjBoMQswCQYDVQQGEwJDTjEUMBIGA1UEChML\nT3Blbkhhcm1vbnkxGTAXBgNVBAsTEE9wZW5IYXJtb255IFRlYW0xKDAmBgNVBAMT\nH09wZW5IYXJtb255IEFwcGxpY2F0aW9uIFJlbGVhc2UwWTATBgcqhkjOPQIBBggq\nhkjOPQMBBwNCAATbYOCQQpW5fdkYHN45v0X3AHax12jPBdEDosFRIZ1eXmxOYzSG\nJwMfsHhUU90E8lI0TXYZnNmgM1sovubeQqATo1IwUDAfBgNVHSMEGDAWgBTbhrci\nFtULoUu33SV7ufEFfaItRzAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFPtxruhl\ncRBQsJdwcZqLu9oNUVgaMAwGCCqGSM49BAMDBQADaAAwZQIxAJta0PQ2p4DIu/ps\nLMdLCDgQ5UH1l0B4PGhBlMgdi2zf8nk9spazEQI/0XNwpft8QAIwHSuA2WelVi/o\nzAlF08DnbJrOOtOnQq5wHOPlDYB4OtUzOYJk9scotrEnJxJzGsh/\n-----END CERTIFICATE-----\n",
"bundle-name": "com.OpenHarmony.app.test",
"apl": "system_core",
"app-feature": "hos_system_app"
},
"acls": {
"allowed-acls": [
""
]
},
"permissions": {
"restricted-permissions": [
""
]
},
"debug-info": {
"device-ids": [
"69C7505BE341BDA5948C3C0CB44ABCD530296054159EFE0BD16A16CD0129CC42",
"7EED06506FCE6325EB2E2FAA019458B856AB10493A6718C7679A73F958732865"
],
"device-id-type": "udid"
},
"issuer": "pki_internal"
}
{
"version-name":"2.0.0",
"version-code":2,
"app-distribution-type":"os_integration",
"uuid":"5027b99e-5f9e-465d-9508-a9e0134ffe18",
"validity":{
"not-before":1594865258,
"not-after":1689473258
},
"type":"release",
"bundle-info":{
"developer-id":"OpenHarmony",
"distribution-certificate":"-----BEGIN CERTIFICATE-----\nMIICFDCCAZugAwIBAgIIf129GJfWkSAwCgYIKoZIzj0EAwMwYzELMAkGA1UEBhMC\nQ04xFDASBgNVBAoTC09wZW5IYXJtb255MRkwFwYDVQQLExBPcGVuSGFybW9ueSBU\nZWFtMSMwIQYDVQQDExpPcGVuSGFybW9ueSBBcHBsaWNhdGlvbiBDQTAeFw0yNjA2\nMDIxMjQ0NTNaFw0zNjA1MzAxMjQ0NTNaMEoxFTATBgNVBAMMDGlkZV9kZW1vX2Fw\ncDENMAsGA1UECxMEVW5pdDEVMBMGA1UEChMMT3JnYW5pemF0aW9uMQswCQYDVQQG\nEwJDTjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAadneFnMAy4jsLRp9zBpuc+\nulvu2PMMJEMsucX34EmzyLooD0a0xXxR/eEInAtdwCOKH8wD/vY6oG8PlfkCQ9Cj\nUjBQMB0GA1UdDgQWBBS4XOVPp6HOAJqVIfOPgcLM2A/WXDAOBgNVHQ8BAf8EBAMC\nB4AwHwYDVR0jBBgwFoAU24a3IhbVC6FLt90le7nxBX2iLUcwCgYIKoZIzj0EAwMD\nZwAwZAIwBr5aBax0JNhhCV9G9fN+sJ48Jwb7l0taE336iq0An7EYPH/1xRDOa64S\nPAPkB8zEAjBzMxXPM2LQ4+EliVZh5tb+4RCwkYJ64MPJgsqaDN2js9lPL7cXHAWL\n1O0mLzdaJ/g=\n-----END CERTIFICATE-----\n",
"bundle-name":"com.example.diraccess",
"apl":"system_core",
"app-feature":"hos_system_app"
},
"acls":{
"allowed-acls":[
""
]
},
"permissions":{
"restricted-permissions":[
]
},
"issuer":"pki_internal"
}
