HarmonyOS 鸿蒙Next PC文件适配最佳实践
HarmonyOS 鸿蒙Next PC文件适配最佳实践
1. API 使用指导
文件操作主要涉及Core File Kit(文件基础服务)提供的接口进行文件的选择,保存,持久化等操作。若应用需要通过自定义的文件选择器访问路径,则需要使用访问控制模块提供的授权申请能力获取权限。
1.1 Core File Kit
1.1.1 文件URI
FileUri
文件的URI,根据问卷路径不同URI会有所区别:
- 应用沙箱uri:file://<bundleName>/<sandboxPath>;
- 公共目录文件类uri:file://docs/storage/Users/currentUser/<publicPath>;
- 公共目录媒体类uri:file://media/<mediaType>/IMG_DATATIME_ID/<displayName>;
getUriFromPath
通过传入的路径path生成应用自己的uri(不支持媒体类型uri的获取);将path转uri时,路径中的中文及非数字字母的特殊字符将会被编译成对应的ASCII码,拼接在uri中。
参考链接:[@ohos.file.fileuri (文件URI)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-fileuri)
1.1.2 选择器
select
通过选择模式拉起documentPicker界面,用户可以选择一个或多个文件。接口采用callback异步返回形式,传入参数DocumentSelectOptions对象,返回选择文件的uri数组。
参考链接:[@ohos.file.picker (选择器)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-picker)
1.1.3 文件分享
persistPermission
异步方法对所选择的多个文件或目录URI持久化授权,以promise形式返回结果,该接口仅对具有该系统能力的设备开放。
revokePermission
异步方法对所选择的多个文件或目录uri取消持久化授权,以promise形式返回结果,该接口仅对具有该系统能力的设备开放。
activatePermission
异步方法使能多个已经永久授权过的文件或目录,以promise形式返回结果,该接口仅对具有该系统能力的设备开放。
deactivatePermission
异步方法取消使能授权过的多个文件或目录,以promise形式返回结果,该接口仅对具有该系统能力的设备开放。
checkPersistentPermission
异步方法校验所选择的多个文件或目录URI持久化授权,以promise形式返回结果。
参考链接:[@ohos.fileshare (文件分享)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-fileshare)
1.1.4 Core File Kit涉及的参考文档
参考链接:
1.2 访问控制
三方应用需要访问公共目录时,需通过弹窗授权向用户申请授予 Download 目录权限、Documents 目录权限或 Desktop 目录权限。
参考链接:获取并使用公共目录
1.3 AbilityKit
三方应用可通过接口,由系统从已安装的应用中寻找符合要求的应用,打开特定文件。
参考链接:目标方接入步骤
2. 文件操作场景指导及示例
2.1 选择用户文件
2.1.1 通过filepicker提供的select方法拉起指定目录
通过select函数拉起文件管理提供的filepicker。通过filepicker中的DocumentSelectOptions参数,可以指定以下内容:
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| maxSelectNumber | number | 否 | 选择文件最大个数,上限500,有效值范围1-500(选择目录仅对具有该系统能力的设备开放。且目录选择的最大个数为1)。默认值是1。系统能力:SystemCapability.FileManagement.UserFileService |
| defaultFilePathUri | string | 否 | 指定选择的文件或者目录路径。 |
| fileSuffixFilters | Array | 否 | 选择文件的后缀类型,传入字符串数组,每一项代表一个后缀选项,每一项内部用“ |
| selectMode | DocumentSelectMode | 否 | 支持选择的资源类型。比如:文件、文件夹和二者混合,仅对具有该系统能力的设备开放,默认值是文件类型。仅支持2in1设备。系统能力:SystemCapability.FileManagement.UserFileService.FolderSelection |
| authMode | boolean | 否 | 拉起授权picker,默认为false(非授权模式)。当authMode为true时为授权模式,defaultFilePathUri必填,表明待授权uri。仅支持2in1设备。系统能力:SystemCapability.FileManagement.UserFileService.FolderSelection |
拉起文档目录示例
以下示例指定了拉起filepicker时的默认路径为Document路径,同时指定了可选择的文件后缀。选择完成后,该应用会在Document目录下生成test1文件夹。
//...
public PullDocPicker() {
try {
let documentSelectOptions = new picker.DocumentSelectOptions();
documentSelectOptions.defaultFilePathUri = 'file://docs/storage/Users/currentUser/Documents/'
documentSelectOptions.selectMode = picker.DocumentSelectMode.MIXED;
documentSelectOptions.fileSuffixFilters =
['文字/Word格式(*.doc *docx)|*.doc,*docx', '.*', '.docx', '.doc', '.pptx', '.ppt'];
documentSelectOptions.fileSuffixFilters = []
let documentPicker = new picker.DocumentViewPicker();
documentPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'FilePickerManager documentSelectResult uri %{public}s',
JSON.stringify(documentSelectResult));
fs.mkdir('/storage/Users/currentUser/Documents/test1')
}).catch((err: BusinessError) => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'DocumentViewPicker.select failed with err %{public}s',
JSON.stringify(err));
});
} catch (error) {
let err: BusinessError = error as BusinessError;
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'DocumentViewPicker failed with err %{public}s',
JSON.stringify(err));
}
}
//...
效果示例

2.1.2 通过startAbility方法拉起指定目录
可以调用startAbility接口拉起filepicker,并指定拉起的默认路径。
拉起用户根目录示例
//...
public PullCustomPicker() {
let want: Want = {
deviceId: '',
bundleName: 'com.huawei.hmos.filemanager',
abilityName: 'MainAbility'
};
want.parameters = {
'fileUri': 'file://docs/storage/Users/currentUser'
};
let context = getContext(this) as common.UIAbilityContext;
context.startAbility(want, (error: BusinessError) => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'PullCustomPicker failed with err %{public}s',
JSON.stringify(error));
})
}
//...
效果示例

2.1.3 申请3D目录权限
公共目录获取接口仅用于获取公共目录路径,不对公共目录访问权限进行校验。若需访问公共目录需申请对应的公共目录访问权限。三方应用需要访问公共目录时,需通过弹窗授权向用户申请授予 Download 目录权限、Documents 目录权限或 Desktop目录权限,具体参考访问控制-向用户申请授权。
"requestPermissions" : [
"ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY",
"ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY",
"ohos.permission.READ_WRITE_DESKTOP_DIRECTORY",
]
获取文档目录权限示例
//在应用窗口拉起时进行权限的申请,实际场景下可以在需要的时候再通过requestPermission操作申请
onWindowStageCreate(windowStage: window.WindowStage): void {
//...
const permissions: Array<Permissions> = ['ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY'];
permissionManager.getInstance().checkPermissions(permissions,this.context);
//SLEEPING
}
//...
public async checkPermissions(permissions: Array<Permissions>, context: common.UIAbilityContext) {
let grantStatus: abilityAccessCtrl.GrantStatus = await this.checkPermissionGrant(permissions[0]);
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
// 已经授权,可以继续访问目标操作
hilog.info(0x0000, PermissionManager.TAG_LOG, 'checkPermissions', 'already granted');
} else {
// 申请权限
this.reqPermissionsFromUser(permissions,context);
}
}
//...
public reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
let grantStatus: Array<number> = data.authResults;
let length: number = grantStatus.length;
for (let i = 0; i < length; i++) {
if (grantStatus[i] === 0) {
// 用户授权,可以继续访问目标操作
} else {
// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
atManager.requestPermissionOnSetting(context, ['ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY']).then((data: Array<abilityAccessCtrl.GrantStatus>) => {
hilog.info(0x0000, PermissionManager.TAG_LOG, 'reqPermissionsFromUser', 'data:' + JSON.stringify(data));
}).catch((err: BusinessError) => {
hilog.info(0x0000, PermissionManager.TAG_LOG, 'reqPermissionsFromUser', 'data:' + JSON.stringify(err));
});
return;
}
}
// 授权成功
}).catch((err: BusinessError) => {
console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
hilog.info(0x0000, PermissionManager.TAG_LOG, 'checkPermissions', 'already granted');
})
}
效果示例

2.2 保存用户文件
2.2.1 通过filepicker提供的save方法保存文件
通过save函数拉起文件管理提供的filepicker。通过filepicker中的DocumentSaveOptions参数,可以指定以下内容:
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| maxSelectNumber | number | 否 | 选择文件最大个数,上限500,有效值范围1-500(选择目录仅对具有该系统能力的设备开放。且目录选择的最大个数为1)。默认值是1。系统能力:SystemCapability.FileManagement.UserFileService |
| defaultFilePathUri | string | 否 | 指定选择的文件或者目录路径。 |
| fileSuffixFilters | Array | 否 | 选择文件的后缀类型,传入字符串数组,每一项代表一个后缀选项,每一项内部用“ |
| selectMode | DocumentSelectMode | 否 | 支持选择的资源类型。比如:文件、文件夹和二者混合,仅对具有该系统能力的设备开放,默认值是文件类型。仅支持2in1设备。系统能力:SystemCapability.FileManagement.UserFileService.FolderSelection |
| authMode | boolean | 否 | 拉起授权picker,默认为false(非授权模式)。当authMode为true时为授权模式,defaultFilePathUri必填,表明待授权uri。仅支持2in1设备。系统能力:SystemCapability.FileManagement.UserFileService.FolderSelection |
保存文件示例:
//...
public SaveFileByPicker() {
try {
let desktopDirUri: string = 'file://docs/storage/Users/currentUser/Desktop/';
let documentSaveOptions = new picker.DocumentSaveOptions();
documentSaveOptions.newFileNames = ['1.txt'];
documentSaveOptions.defaultFilePathUri = desktopDirUri;
documentSaveOptions.pickerMode = picker.DocumentPickerMode.DEFAULT;
let documentPicker = new picker.DocumentViewPicker();
documentPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'FilePickerManager documentSaveResult uri %{public}s',
JSON.stringify(documentSaveResult));
}).catch((err: BusinessError) => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'SaveFileByPicker.select failed with err %{public}s',
JSON.stringify(err));
});
} catch (error) {
let err: BusinessError = error as BusinessError;
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'SaveFileByPicker failed with err %{public}s',
JSON.stringify(err));
}
}
//...
效果示例:

2.3 持久化授权
需要持久化授权能力的主要场景如下:
| 场景 | 典型应用示例 |
|---|---|
| 先使用A应用打开B文件后,关闭A应用。再次打开A应用时,通过历史记录打开B文件。 | 通过应用侧的历史记录打开文档。 |
| 使用IM类应用A接收/发送文件后,双击使用应用B打开文件浏览编辑。 | 打开通过QQ发送的word文档。 |
| 在A应用的文件中插入附件/超链接后,双击附件/超链接使用应用B打开。 | 打开亿图图示中的超链接。 |
| 通过拖拽/复制粘贴添加到A应用中的文件尝试通过B应用打开。 | 打开通过拖拽/复制粘贴到QQ中发送的文件。 |
2.3.1 对路径进行持久化授权
在以上描述的场景中,需要调用persistPermission接口对文件或者目录的路径进行持久化授权,从而确保应用可以在之后用到时可以持有访问的权限。文件持久化能力需要获取FILE_ACCESS_PERSIST权限。权限申请流程参考访问控制-申请应用权限。
//module.json5
//...
"requestPermissions": [
{
"name": "ohos.permission.FILE_ACCESS_PERSIST",
}
//...
持久化授权示例:
//...
private CustomPersistPermission(targetUri: string, targetMode: fileShare.OperationMode) {
let policyInfo: fileShare.PolicyInfo = {
uri: targetUri,
operationMode: targetMode,
};
let policies: Array<fileShare.PolicyInfo> = [policyInfo];
fileShare.persistPermission(policies).then(() => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, '%{public}s', 'CustomPersistPermission successfully');
}).catch((err: BusinessError<Array<fileShare.PolicyErrorResult>>) => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'CustomPersistPermission failed with err %{public}s',
JSON.stringify(err));
});
}
2.3.2 激活已经持有的持久化授权
已经进行持久化授权的文件或路径,在每一次需要访问前,需要激活已经持有的持久化权限。
//...
private CustomActivatePermission(targetUri: string, targetMode: fileShare.OperationMode) {
let policyInfo: fileShare.PolicyInfo = {
uri: targetUri,
operationMode: targetMode,
};
let policies: Array<fileShare.PolicyInfo> = [policyInfo];
fileShare.activatePermission(policies).then(() => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, '%{public}s', 'CustomActivatePermission successfully');
}).catch((err: BusinessError<Array<fileShare.PolicyErrorResult>>) => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'CustomActivatePermissionfailed with err %{public}s',
JSON.stringify(err));
});
}
2.3.3 取消已经持有的持久化授权
已经进行持久化授权的文件或路径,可以通过以下方式取消。
//...
private CustomRevokePermission(targetUri: string, targetMode: fileShare.OperationMode) {
let policyInfo: fileShare.PolicyInfo = {
uri: targetUri,
operationMode: targetMode,
};
let policies: Array<fileShare.PolicyInfo> = [policyInfo];
fileShare.revokePermission(policies).then(() => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, '%{public}s', 'CustomRevokePermission successfully');
}).catch((err: BusinessError<Array<fileShare.PolicyErrorResult>>) => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'CustomRevokePermission failed with err %{public}s',
JSON.stringify(err));
});
}
2.3.4 取消已经激活的持久化授权
已经激活了持久化授权的文件或路径,可以通过以下方式取消激活状态。
//...
private CustomDeactivatePermission(targetUri: string, targetMode: fileShare.OperationMode) {
let policyInfo: fileShare.PolicyInfo = {
uri: targetUri,
operationMode: targetMode,
};
let policies: Array<fileShare.PolicyInfo> = [policyInfo];
fileShare.deactivatePermission(policies).then(() => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, '%{public}s', 'CustomDeactivatePermission successfully');
}).catch((err: BusinessError<Array<fileShare.PolicyErrorResult>>) => {
hilog.info(0x0000, FilePickerManager.TAG_LOG, 'CustomDeactivatePermission failed with err %{public}s',
JSON.stringify(err));
});
}
2.4 跨应用预览用户文件
以下应用类型存在跨应用预览用户文件的场景:
- IM类应用:发送文件/接收文件后,尝试使用其他应用预览/编辑文件。
- 文档类应用:文件以附件/超链接形式存在,尝试使用其他应用预览/编辑文件。
2.4.1 通过filepicker插入文件场景
通过系统提供的filepicker能力插入文件后,应用即可获取该文件的临时访问权限。在进行持久化授权后,即可分享给其他应用打开该文件。以下代码示例在选择文件后,进行了读权限的持久化授权,并分享给预览应用打开文件。在实际应用场景中,可以灵活指定需要的权限和需要分享给的目标应用。
//...
let uriTemp: string = '';
try {
// 1.拉起系统picker
let documentSelectOptions = new picker.DocumentSelectOptions();
let documentPicker = new picker.DocumentViewPicker;
documentPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
uriTemp = documentSelectResult[0];
hilog.info(0x0000, FileShareManager.TAG_LOG, 'SharePublicFile documentSelectResult uri %{public}s', uriTemp);
// 2.持久化授权
let policyInfo: fileShare.PolicyInfo = {
uri: uriTemp,
operationMode: fileShare.OperationMode.READ_MODE,
};
let policies: Array<fileShare.PolicyInfo> = [policyInfo];
fileShare.persistPermission(policies).then(() => {
hilog.info(0x0000, FileShareManager.TAG_LOG, 'SharePublicFile persistPermission successfully');
}).catch((err: BusinessError<Array<fileShare.PolicyErrorResult>>) => {
hilog.info(0x0000, FileShareManager.TAG_LOG,
'SharePublicFile persistPermission failed with error message %{public}s', JSON.stringify(err));
});
// 3.分享文件
let want: Want = {
// 配置被分享文件的读写权限,例如对被分享应用进行读写授权
flags: wantConstant.Flags.FLAG_AUTH_WRITE_URI_PERMISSION | wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION,
// 只配置读权限 flags:wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION,
// 配置分享应用的隐式拉起规则 action: 'ohos.want.action.sendData',
action: 'ohos.want.action.viewData',
uri: uriTemp,
type: 'application/pdf'
}
let context = getContext(this) as common.UIAbilityContext;
context.startAbility(want, (error: BusinessError) => {
hilog.info(0x0000, FileShareManager.TAG_LOG, 'SharePublicFile result code %{public}s', JSON.stringify(error));
})
}).catch((err: BusinessError) => {
hilog.info(0x0000, FileShareManager.TAG_LOG, 'SharePublicFile.select failed with err %{public}s',
JSON.stringify(err));
});
} catch (error) {
let err: BusinessError = error as BusinessError;
hilog.info(0x0000, FileShareManager.TAG_LOG, 'SharePublicFile failed with err %{public}s',
JSON.stringify(err));
}
2.4.2 通过拖拽插入文件场景
通过拖拽插入文件后,应用即可获取该文件的临时访问权限。在进行持久化授权后,即可分享给其他应用打开该文件。
参考demo:
//...
import { uniformDataStruct, uniformTypeDescriptor, unifiedDataChannel } from '@kit.ArkData';
import { fileUri, fileIo as fs } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'
@Entry
@Component
struct Index {
@State ondrop_uri: string = '';
@State description:string = '';
uiContext = this.getUIContext();
udKey: string = '';
build() {
Column() {
Column() {
Text("拖出方")
Text("oriUri:" + 'file://data/image/1.png')
Text("description:" + 'This is the description of the hyperlink')
}
.width(300)
.height(200)
.borderWidth(2)
.margin(20)
.onDragStart((event) => {
//创建FILE_URI类型数据
let fileUriDetails : Record<string, string> = {
'attr1': 'value1',
'attr2': 'value2',
}
let fileUri : uniformDataStruct.FileUri = {
uniformDataType : 'general.file-uri',
oriUri : 'file://data/image/1.png',
fileType : 'general.image',
details : fileUriDetails,
}
//创建HYPERLINK类型数据
let hyperlink : uniformDataStruct.Hyperlink = {
uniformDataType:'general.hyperlink',
url : 'file://data/image/1.png',
description : 'This is the description of the hyperlink',
}
//新建一个新的unifiedData
let unifiedData = new unifiedDataChannel.UnifiedData();
//新建一个UnifiedRecord 初始化塞入FORM类型的数据(可自定义)
let record = new unifiedDataChannel.UnifiedRecord(uniformTypeDescriptor.UniformDataType.HYPERLINK, hyperlink);
//addEntry 添加另一份FILE_URI数据到record里面
record.addEntry(uniformTypeDescriptor.UniformDataType.FILE_URI, fileUri);
//将record塞入unifiedData
unifiedData.addRecord(record);
//将unifiedData塞入dragEvent
event.setData(unifiedData)
})
Button("Reset")
.onClick(() =>{
this.ondrop_uri = ''
this.description = ''
})
Column() {
Text("落入方")
Text("oriUri:" + this.ondrop_uri)
Text("description:" + this.description)
}
.width(300)
.height(200)
.borderWidth(2)
.margin(20)
.onDrop((event) =>{
//配置异步拖拽参数
let context = this.uiContext.getHostContext() as common.UIAbilityContext;
let pathDir: string = context.distributedFilesDir;
let destUri = fileUri.getUriFromPath(pathDir);
//配置数据处理回调
let progressListener: unifiedDataChannel.DataProgressListener = (progress: unifiedDataChannel.ProgressInfo, dragData: UnifiedData|null) => {
if(dragData != null) {
//拿到records
let records:Array<unifiedDataChannel.UnifiedRecord> = dragData.getRecords();
//循环遍历查询
for (let i = 0; i < records.length; i++) {
let unifiedDataRecord = records[i] as unifiedDataChannel.UnifiedRecord;
//找到对应类型的数据 getEntry
let fileUriRead : uniformDataStruct.FileUri = unifiedDataRecord.getEntry(uniformTypeDescriptor.UniformDataType.FILE_URI) as uniformDataStruct.FileUri;
if (fileUriRead != undefined) {
//拿到数据类型中对应的字段
console.info(`oriUri: ${fileUriRead.oriUri}`);
this.ondrop_uri = fileUriRead.oriUri;
}
let hyperlink = unifiedDataRecord.getEntry(uniformTypeDescriptor.UniformDataType.HYPERLINK) as uniformDataStruct.Hyperlink;
if (hyperlink != undefined) {
console.info(`description: ${hyperlink.description}`);
this.description = hyperlink.description as string;
}
}
} else {
console.log('dragData is undefined');
}
console.log(`percentage: ${progress.progress}`);
};
//配置异步获取数据options
let options: DataSyncOptions = {
destUri: destUri,
fileConflictOptions: unifiedDataChannel.FileConflictOptions.OVERWRITE, //配置数据冲突处理方式
progressIndicator: unifiedDataChannel.ProgressIndicator.DEFAULT, //配置进度条,数据处理超过500ms生效
dataProgressListener: progressListener,
}
try {
this.udKey = (event as DragEvent).startDataLoading(options);
console.log('udKey: ', this.udKey);
} catch(e) {
console.log(`startDataLoading errorCode: ${e.code}, errorMessage: ${e.message}`);
}
}, {disableDataPrefetch: true}) //当使用startDataLoading获取数据时需设置该参数为true,防止拖拽提前获取数据。
Button('取消数据传输')
.onClick(() => {
try {
this.getUIContext().getDragController().cancelDataLoading(this.udKey);
} catch (e) {
console.log(`cancelDataLoading errorCode: ${e.code}, errorMessage: ${e.message}`);
}
})
.margin({top: 10})
}
.height('100%')
.width('100%')
}
}
2.4.3 通过剪贴板插入文件场景
通过剪贴板插入文件后,应用即可获取该文件的临时访问权限。在进行持久化授权后,即可分享给其他应用打开该文件。
2.5 文件格式关联
2.5.1 打开特定后缀的文件场景
应用作为特定文件格式打开的目标方,适配已达到可以在打开方式中被选择的目的。
参考链接:目标方接入步骤。
2.5.2 注册右键扩展菜单场景
三方应用可以接入文件管理内右键菜单,并指定操作。在应用工程对应的待注册右键菜单的模块的module.json5文件中,在”module”下新增子节点”fileContextMenu”,配置值为指定的右键配置文件,示例如下:
//module.json5
//...
"module":{
"name": "entry",
"type": "entry",
"mainElement": "EntryAbility",
"fileContextMenu": "$profile:menu",
//...
此含义为右键配置文件在此模块的resources\base\profile目录下,文件名为menu.json。右键菜单的注入方式核心重点主要是配置文件的编写,配置文件的名字当前无限制,只要和第一步中指定的文件名保持一致即可,放置位置也需要按照配置指定目录存放,即开发视图的resources\base\profile目录下。
下表为配置文件标签说明
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
|---|---|---|---|
| fileContextMenu | 标识当前module注册右键菜单的数量。(单模块和单应用注册数量不能超过5个,配置超过数量文管当前只解析随机5个) | 对象数组 | 不可缺省 |
下表为rightMenu标签配置说明
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
|---|---|---|---|
| abilityName | 表示当前右键菜单对应的需要拉起的ability名称 | 字符串 | 不可缺省 |
| menuItem | 右键菜单显示的文件信息 | 资源id | 不可缺省 |
| menuHandler | 一个ability可以创建多个右键菜单,用该字段来区分用户拉起的不同右键菜单项。该字段在用户点击右键菜单执行时,会作为参数传递给右键菜单应用 | 字符串 | 不可缺省 |
| menuContext | 定义 |
更多关于HarmonyOS 鸿蒙Next PC文件适配最佳实践的实战教程也可以访问 https://www.itying.com/category-93-b0.html
鸿蒙Next PC文件适配主要涉及分布式文件系统能力。开发者需使用HarmonyOS提供的File API实现跨设备文件访问,重点关注fileio和statfs模块。适配要点:
- 使用
ohos.file.fs处理文件操作; - 通过分布式文件管理实现设备间文件共享;
- 注意URI格式统一采用"datashare://"协议。
关键接口包括openSync、readSync等同步操作接口。权限配置需在config.json声明所需文件访问权限。文件路径建议使用系统预置目录常量如DIR_DOCUMENTS。
更多关于HarmonyOS 鸿蒙Next PC文件适配最佳实践的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
针对HarmonyOS Next PC文件适配,以下是关键实践要点:
- 文件URI处理
- 使用
@ohos.file.fileuri模块处理不同路径类型URI:
// 沙箱路径转URI
let uri = fileUri.getUriFromPath('/data/storage/el2/base/files/test.txt');
- 文件选择器
- 通过
DocumentViewPicker实现文件选择/保存:
// 文件选择
let picker = new picker.DocumentViewPicker();
picker.select({fileSuffixFilters: ['.pdf']}).then((uris) => {
// 处理选中文件
});
- 持久化授权
- 跨应用访问需持久化授权:
// 持久化读权限
fileShare.persistPermission([{
uri: fileUri,
operationMode: fileShare.OperationMode.READ_MODE
}]);
- 公共目录访问
- 申请对应目录权限:
// module.json5配置
"requestPermissions": [
"ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY"
]
- 文件分享
- 使用Want启动关联应用:
let want = {
action: 'ohos.want.action.viewData',
uri: fileUri,
type: 'application/pdf'
};
context.startAbility(want);
- 回收站操作
- 删除文件到回收站:
fileManagerService.deleteToTrash(fileUri).then((trashUri) => {
console.info(`Moved to trash: ${trashUri}`);
});
- 右键菜单扩展
- 注册文件关联操作:
// profile/menu.json
{
"abilityName": "MainAbility",
"menuItem": "$string:open_with",
"menuContext": [{
"menuKind": 1,
"fileSupportType": [".docx"]
}]
}
关键注意事项:
- 媒体文件使用
file://media/前缀URI - 持久化授权需声明
ohos.permission.FILE_ACCESS_PERSIST权限 - 拖拽文件时需激活临时权限
- 卸载应用会自动清除相关授权
建议参考完整示例代码实现具体功能场景。

