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

5 回复

在鸿蒙系统中,使用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"错误,可能由以下原因及解决方案导致:

一、核心原因分析

  1. 未正确使用弹窗授权返回的URI 调用showAssetsCreationDialog获取的URI是媒体库授予写入权限的目标路径,必须使用此URI执行写入操作。若仍直接操作应用沙箱路径或尝试其他写入方式,会导致权限错误。

  2. 文件写入操作未使用文件描述符(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}`);
  }
}

三、关键注意事项

  1. 路径有效性检查

    • 源文件URI必须是应用沙箱路径(格式:file://com.example.app/...),否则弹窗无法预览且写入会失败。
    • 若源文件不存在,需先确保文件已正确下载/生成到沙箱内。
  2. 模块配置要求 调用showAssetsCreationDialog前,需在module.json5abilities标签中配置labelicon,否则弹窗无法显示应用名称导致授权异常:

    "abilities": [{
      "name": "EntryAbility",
      "icon": "$media:icon",
      "label": "$string:app_name"
    }]
    
  3. 大文件处理(>25MB) 若图片超过25MB:

    • 避免使用packing接口压缩
    • 改用packToFile接口处理后再保存

四、替代方案:安全控件保存 若需避免弹窗,可使用SaveButton安全控件(需UI符合规范):

import { SaveButton } from '@ohos.security.savetoken';

build() {
  SaveButton({
    onSaveClick: async () => {
      // 保存逻辑同上(仍需使用fd复制)
    }
  })
  .width(100).height(40)
}

提示:若问题仍未解决,请检查:

  1. 是否在applyChanges后未等待异步操作完成
  2. 设备存储空间是否已满
  3. 是否在module.json5中声明了ohos.permission.READ_IMAGEVIDEO权限

在HarmonyOS Next中,使用photoAccessHelper保存图片到相册时,即使弹窗授权后仍报错“Operation not permitted”,通常是由于权限配置或访问流程问题导致。

首先,请确保在module.json5文件中已正确声明ohos.permission.READ_IMAGEVIDEOohos.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}`);
}

关键点说明:

  1. showAssetsCreationDialog 返回的 desFileUris 是一个“位置URI”(例如,代表相册或目录),而不是一个可以直接打开写入的文件URI。
  2. photoAccessHelper.createAsset 方法接收这个“位置URI”、源文件URI和配置信息,它会处理文件的创建、数据写入以及媒体库数据库的更新。
  3. 直接使用 fileIo.open 尝试打开一个由 showAssetsCreationDialog 返回的 URI 违反了权限边界,因此系统会抛出 Operation not permitted 错误。

请使用 createAsset 方法来替代手动的文件复制操作,这符合 HarmonyOS Next 的媒体库安全访问规范。

回到顶部