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
授权弹框本意是不需要申请权限,但是在点击禁止以后,再次以同样的方式调起授权弹窗时会一直保存失败

更多关于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 仍然返回了空结果。
这种设计符合权限管理的安全规范:应用需要明确处理用户拒绝权限的情况,不能假设用户最终一定会授权。

