HarmonyOS鸿蒙Next中使用photoAccessHelper保存图片到相册,弹窗授权后仍报错:Operation not permitted
HarmonyOS鸿蒙Next中使用photoAccessHelper保存图片到相册,弹窗授权后仍报错:Operation not permitted
try {
// 保存至相册中
const context: Context = this.getUIContext().getHostContext() as Context;
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context)
let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
{
title: 'test', // 可选
fileNameExtension: 'png',
photoType: photoAccessHelper.PhotoType.IMAGE,
subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可选
}
];
let srcFileUris: Array<string> = [
srcFileUri
];
// 基于弹窗授权的方式获取媒体库的目标uri
let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
// 将来源于应用沙箱的照片内容写入媒体库的目标uri
let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY);
let srcFile: fileIo.File = await fileIo.open(srcFileUri, fileIo.OpenMode.READ_ONLY);
await fileIo.copyFile(srcFile.fd, desFile.fd);
fileIo.closeSync(srcFile);
fileIo.closeSync(desFile);
console.info('create asset by dialog successfully');
} catch (error) {
console.error(`error is ${error}`)
}
有弹窗并点击了允许,desFileUris也有返回uri,但是接下去执行 fileIo.open(desFileUris[0] 就catch error了:
error = Error: Operation not permitted@8f58bf1
code = 13900001
点击允许不是就是授权了吗?为什么还会报这样的错呢?
更多关于HarmonyOS鸿蒙Next中使用photoAccessHelper保存图片到相册,弹窗授权后仍报错:Operation not permitted的实战教程也可以访问 https://www.itying.com/category-93-b0.html
在鸿蒙系统中,使用photoAccessHelper保存图片到相册时,即使通过弹窗授权,仍出现Operation not permitted(错误码 13900001),核心原因是权限未完全授予或文件操作方式不符合媒体库安全规范。
13900001 操作不允许
错误信息
Operation not permitted
错误描述
操作不允许。
可能原因
当前用户文件操作不被允许,URI或path访问未授权。
处理步骤
1.根据当前系统的访问控制机制,应用无法使用分享给其他应用的URI。
2.根据系统Picker的运行机制,通过Picker获取到的URI仅有临时权限,无法持久化保存使用。
3.URI路径不推荐进行拼接,拼接后的URI默认未授权。
更多关于HarmonyOS鸿蒙Next中使用photoAccessHelper保存图片到相册,弹窗授权后仍报错:Operation not permitted的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
破案了,这样确实不需要ohos.permission.READ_IMAGEVIDEO权限,但是导出图片到大小是受限的,太大也会Operation not permitted。尝试把图像分辨率调小后可以成功导出到相册。
根据鸿蒙(HarmonyOS)开发文档,使用photoAccessHelper保存图片到相册时出现"Operation not permitted"错误,可能由以下原因及解决方案导致:
一、核心原因分析
-
未正确使用弹窗授权返回的URI 调用
showAssetsCreationDialog获取的URI是媒体库授予写入权限的目标路径,必须使用此URI执行写入操作。若仍直接操作应用沙箱路径或尝试其他写入方式,会导致权限错误。 -
文件写入操作未使用文件描述符(fd) 在复制文件内容时,需通过
fileIo.open()获取源文件和目标文件的文件描述符(fd),再使用fileIo.copyFile()接口传输数据。直接操作路径字符串会触发权限异常。
二、正确代码实现步骤(基于弹窗授权)
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
async function saveImage() {
try {
// 1. 指定应用沙箱内的源图片URI
let srcFileUri = 'file://com.example.app/data/files/image.jpg';
let srcFileUris: string[] = [srcFileUri];
// 2. 配置图片创建选项
let photoCreationConfigs: photoAccessHelper.PhotoCreationConfig[] = [{
photoType: photoAccessHelper.PhotoType.IMAGE,
subtype: photoAccessHelper.PhotoSubtype.DEFAULT // 可选
}];
// 3. 弹窗授权获取目标URI(永久写入权限)
let desFileUris: string[] = await photoAccessHelper.showAssetsCreationDialog(
srcFileUris,
photoCreationConfigs
);
if (desFileUris.length === 0) {
console.error('用户拒绝授权');
return;
}
// 4. 通过fd复制文件内容
let srcFile = await fileIo.open(srcFileUri, fileIo.OpenMode.READ_ONLY);
let desFile = await fileIo.open(desFileUris, fileIo.OpenMode.WRITE_ONLY);
await fileIo.copyFile(srcFile.fd, desFile.fd); // 关键:使用fd复制
// 5. 关闭文件释放资源
await srcFile.close();
await desFile.close();
console.info('图片保存成功');
} catch (err) {
console.error(`保存失败: ${err.code}, ${err.message}`);
}
}
三、关键注意事项
-
路径有效性检查
- 源文件URI必须是应用沙箱路径(格式:
file://com.example.app/...),否则弹窗无法预览且写入会失败。 - 若源文件不存在,需先确保文件已正确下载/生成到沙箱内。
- 源文件URI必须是应用沙箱路径(格式:
-
模块配置要求 调用
showAssetsCreationDialog前,需在module.json5的abilities标签中配置label和icon,否则弹窗无法显示应用名称导致授权异常:"abilities": [{ "name": "EntryAbility", "icon": "$media:icon", "label": "$string:app_name" }] -
大文件处理(>25MB) 若图片超过25MB:
- 避免使用
packing接口压缩 - 改用
packToFile接口处理后再保存
- 避免使用
四、替代方案:安全控件保存 若需避免弹窗,可使用SaveButton安全控件(需UI符合规范):
import { SaveButton } from '@ohos.security.savetoken';
build() {
SaveButton({
onSaveClick: async () => {
// 保存逻辑同上(仍需使用fd复制)
}
})
.width(100).height(40)
}
提示:若问题仍未解决,请检查:
- 是否在
applyChanges后未等待异步操作完成- 设备存储空间是否已满
- 是否在
module.json5中声明了ohos.permission.READ_IMAGEVIDEO权限
在HarmonyOS Next中,使用photoAccessHelper保存图片到相册时,即使弹窗授权后仍报错“Operation not permitted”,通常是由于权限配置或访问流程问题导致。
首先,请确保在module.json5文件中已正确声明ohos.permission.READ_IMAGEVIDEO和ohos.permission.WRITE_IMAGEVIDEO权限。其次,在调用保存接口前,必须使用requestPermissionsFromUser方法动态申请这些权限,并等待用户授权完成。授权弹窗的通过仅表示用户同意,后续代码需在授权回调成功后执行保存操作。此外,请检查使用的PhotoAsset对象或文件路径是否有效,并确认应用已获得相册的写入访问器。
这个错误通常是因为权限作用域问题。showAssetsCreationDialog 弹窗授权返回的 URI 是一个临时的、有作用域限制的 URI,它仅能用于 photoAccessHelper 提供的特定方法(如 createAsset),而不能直接使用 fileIo.open 进行文件操作。
在 HarmonyOS Next 中,媒体库访问遵循更严格的权限模型。showAssetsCreationDialog 的目的是让用户选择一个保存位置,并返回一个代表该位置的 URI。但是,要实际写入数据,你应该使用 photoAccessHelper.createAsset 方法,而不是直接进行文件 IO 操作。
修改方案:
请将你的代码中文件写入部分替换为使用 createAsset 方法。示例如下:
try {
const context: Context = this.getUIContext().getHostContext() as Context;
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
{
title: 'test', // 可选
fileNameExtension: 'png',
photoType: photoAccessHelper.PhotoType.IMAGE,
subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可选
}
];
let srcFileUris: Array<string> = [srcFileUri];
// 1. 获取用户选择的保存位置URI
let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
// 2. 使用 createAsset 方法创建并写入媒体文件
// 注意:这里使用 srcFileUris[0] 作为源文件,desFileUris[0] 作为目标位置
let assetUri: string = await phAccessHelper.createAsset(desFileUris[0], srcFileUris[0], photoCreationConfigs[0]);
console.info('create asset by dialog successfully, assetUri: ' + assetUri);
} catch (error) {
console.error(`error is ${error}`);
}
关键点说明:
showAssetsCreationDialog返回的desFileUris是一个“位置URI”(例如,代表相册或目录),而不是一个可以直接打开写入的文件URI。photoAccessHelper.createAsset方法接收这个“位置URI”、源文件URI和配置信息,它会处理文件的创建、数据写入以及媒体库数据库的更新。- 直接使用
fileIo.open尝试打开一个由showAssetsCreationDialog返回的 URI 违反了权限边界,因此系统会抛出Operation not permitted错误。
请使用 createAsset 方法来替代手动的文件复制操作,这符合 HarmonyOS Next 的媒体库安全访问规范。

