HarmonyOS 鸿蒙Next中使用弹窗授权保存视频,首次拒绝后再次允许失败

HarmonyOS 鸿蒙Next中使用弹窗授权保存视频,首次拒绝后再次允许失败

/**
 * 将本地路径视频保存到相册
 * @param context
 * @param imgPath
 */
public static async saveVideoToAlbum(context: common.UIAbilityContext, imgPath: string,
  callBack: (downloadState: boolean) => void) {
  try {
    // 指定待保存到媒体库的位于应用沙箱的图片uri。
    let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
    let srcFileUri = 'file://'+ Constants.APP_PACKETNAME + imgPath;
    let srcFileUris: Array<string> = [
      srcFileUri
    ];
    // 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选。
    let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
      {
        fileNameExtension: 'mp4',
        photoType: photoAccessHelper.PhotoType.VIDEO,
        subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可选。
      }
    ];
    // 基于弹窗授权的方式获取媒体库的目标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);
    callBack(true)
  } catch (err) {
    callBack(false)
    console.error(`failed to create asset by dialog successfully errCode is: ${err.code}, ${err.message}`);
  }
}

复现路径为首次通过showAssetsCreationDialog弹起授权弹窗时点击禁止 然后再次点击保存使用相同的方法弹出授权弹窗,这时候点击允许就会下载失败捕捉到catch为13900002, No such file or directory,但是首次调起的时候点击允许的时候 是没问题的。


更多关于HarmonyOS 鸿蒙Next中使用弹窗授权保存视频,首次拒绝后再次允许失败的实战教程也可以访问 https://www.itying.com/category-93-b0.html

13 回复

授权弹框本意是不需要申请权限,但是在点击禁止以后,再次以同样的方式调起授权弹窗时会一直保存失败 cke_162.png

更多关于HarmonyOS 鸿蒙Next中使用弹窗授权保存视频,首次拒绝后再次允许失败的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


https://developer.huawei.com/consumer/cn/blog/topic/03190294886533100我发现好像却是是有点问题你看看这篇文章,

不对啊,我之前也用过保存图片的,用到也是保存媒体库资源,但是就算禁止后,再点击按钮后他还是可以在出现弹窗的,,

保存图片没有问题,但是保存视频发生了上述问题,并且点击禁止后,再点击按钮它还是弹出弹窗了 但是这时候点击允许的话就会保存失败,

import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';
import photoAccessHelper from '@ohos.file.photoAccessHelper';
import http from '@ohos.net.http';
import { PromptAction } from '@kit.ArkUI';

@Entry
@Component
struct Index {
  @State videoSrc: string = '';
  public url: string = 'https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720/Big_Buck_Bunny_720_10s_1MB.mp4';
  @State imageBuffer: ArrayBuffer | undefined = undefined;
  public context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  @State saveButtonOptions: SaveButtonOptions = {
    icon: SaveIconStyle.FULL_FILLED,
    text: SaveDescription.SAVE_TO_GALLERY,
    buttonType: ButtonType.Capsule
  };
  private promptAction: PromptAction = this.getUIContext().getPromptAction();
  private TAG: string = "保存视频";

  aboutToAppear(): void {
    // 每一个httpRequest对应一个HTTP请求任务,不可复用
    let httpRequest = http.createHttp();
    let options: http.HttpRequestOptions = {
      method: http.RequestMethod.GET,
      readTimeout: 60000,
      connectTimeout: 60000,
      maxLimit: 100 * 1024 * 1024,
      expectDataType: http.HttpDataType.ARRAY_BUFFER
    };

    httpRequest
      .request(this.url, options,
        async (error: BusinessError, data: http.HttpResponse) => {
          if (error) {
            console.error(this.TAG, 'HTTP请求失败');
          }
          if (data.result instanceof ArrayBuffer) {
            this.imageBuffer = data.result as ArrayBuffer;
          }
        }
      );
  }

  build() {
    Column() {
      SaveButton(this.saveButtonOptions) // 创建安全控件按钮
        .onClick(async (event, result: SaveButtonOnClickResult) => {
          if (result == SaveButtonOnClickResult.SUCCESS) {
            try {
              // 1、使用安全控件创建文件
              let phAccessHelper: photoAccessHelper.PhotoAccessHelper =
                photoAccessHelper.getPhotoAccessHelper(this.context);
              let options: photoAccessHelper.CreateOptions = {
                title: Date.now().toString()
              };
              let photoUri: string =
                await phAccessHelper.createAsset(photoAccessHelper.PhotoType.VIDEO, 'mp4', options);
              console.warn(this.TAG, `创建媒体资源成功,photoUri:${photoUri}`);
              let file = fs.openSync(photoUri, fs.OpenMode.WRITE_ONLY);
              fs.writeSync(file.fd, this.imageBuffer); //图片的buffer数据
              fs.closeSync(file);
              this.promptAction.showToast({
                message: '保存成功',

              });
            } catch (error) {
              let err = error as BusinessError;
              console.error(this.TAG, `保存视频失败:${JSON.stringify(err)}`);
            }
          } else {
            console.error(this.TAG, '安全控件点击结果异常,创建媒体资源失败');
          }
        });
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)

  }
}

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

再次向用户申请授权是使用requestPermissionsFromUser()拉起弹框请求用户授权吗,如果是 的话应该是可以成功的

那就需要申请受限开放权限WRITE_IMAGEVIDEO? 本意是不想再去申请权限,

直接用就行吧,不用申请吧,而且如果之前没有申请,那第一次是怎么使用弹窗授权保存视频,

WRITE_IMAGEVIDEO属于受限开放权限,需要申请证书的时候申请。它系统提供的showAssetsCreationDialog方式本意是这个API能满足你的功能就不需要申请权限了,但是现在出现了我所描述的问题😭😭😭,

在HarmonyOS Next中,首次拒绝弹窗授权后再次调用保存视频权限,系统会默认禁止并可能不再触发授权请求。这是因为系统权限管理策略限制了重复请求。开发者需要在代码中处理用户拒绝后的场景,通过检查权限状态并引导用户手动前往系统设置开启对应权限,无法直接再次弹出授权框。

这个问题是由于首次拒绝授权后,showAssetsCreationDialog 返回的 desFileUris 数组为空导致的。当用户首次拒绝授权时,系统不会创建目标文件,因此后续调用即使授权成功,返回的 URI 也无法指向有效的文件路径,从而在 fileIo.open 时抛出 13900002 错误。

解决方案:

在调用 fileIo.open 之前,需要检查 desFileUris 数组的有效性。如果数组为空或第一个元素无效,应直接处理为授权失败,而不是继续执行文件操作。

修改后的核心代码如下:

let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);

// 关键修复:检查授权对话框返回的URI数组是否有效
if (!desFileUris || desFileUris.length === 0) {
    callBack(false);
    console.error('User denied authorization or no URI returned.');
    return;
}

// 确保URI有效后再进行文件操作
let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY);

原因分析:

showAssetsCreationDialog 方法的行为是:用户授权允许时,返回有效的目标文件URI数组;用户拒绝授权或取消操作时,返回空数组或失败。你的代码未处理拒绝授权后的空返回值,导致后续流程试图打开一个不存在的文件路径。

建议的完整错误处理逻辑:

catch 块之前增加对 desFileUris 的判空检查,可以更清晰地分离“用户拒绝授权”和“文件操作失败”两种错误场景。首次拒绝后再次允许失败,正是因为代码没有处理中间状态——虽然第二次用户点击了“允许”,但可能因为系统策略或会话状态,showAssetsCreationDialog 仍然返回了空结果。

这种设计符合权限管理的安全规范:应用需要明确处理用户拒绝权限的情况,不能假设用户最终一定会授权。

回到顶部