HarmonyOS 鸿蒙Next中动态图片保存至图库报错

HarmonyOS 鸿蒙Next中动态图片保存至图库报错 cke_211.png

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/photoaccesshelper-movingphoto#%E4%BF%9D%E5%AD%98%E5%8A%A8%E6%80%81%E7%85%A7%E7%89%87%E8%B5%84%E6%BA%90

按照上面的指导,但是出现报错:create moving photo failed with error: 401, Failed to check video resource of moving photo,如何解决?


更多关于HarmonyOS 鸿蒙Next中动态图片保存至图库报错的实战教程也可以访问 https://www.itying.com/category-93-b0.html

10 回复

尊敬的开发者,您好, 关于您的反馈的问题, 根据您提供的错误信息“create moving photo failed with error: 401, Failed to check video resource of moving photo”,错误码401在HarmonyOS开发中通常表示参数检查失败参数非法。

结合动态图片开发流程,应重点检查与视频资源相关的参数完整性与有效性,URI格式是否符合规范,同时确保该URI指向的文件存在且未被损坏。 推荐使用以下形式来确保URI格式规范性:

let imageFileUri = 'file://' + context.filesDir + '/imported_photo.jpg';
let videoFileUri = 'file://' + context.filesDir + '/imported_photo.mp4';

后续有任何问题,提供更详细的错误记录和信息有助于帮助您解决问题,欢迎您随时提问。

更多关于HarmonyOS 鸿蒙Next中动态图片保存至图库报错的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


视频资源检查不通过,视频不要超过10秒。

cke_267.png

把附件下载后去掉.txt,放resource目录下的resfile(没有建一个)里,跑下面代码就可以在相册生成动态图片。

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { common } from '@kit.AbilityKit';
// ...

@Entry
@Component
export struct Scene1 {
  @State statusMessage: string = '';
  @State imageSource: string = '';

  saveButtonOptions: SaveButtonOptions = {
    icon: SaveIconStyle.FULL_FILLED,
    text: SaveDescription.SAVE_IMAGE,
    buttonType: ButtonType.Capsule
  }// Set properties of SaveButton.

  // ...

  build() {
    NavDestination() {
      Column({ space: 20 }) {
        SaveButton(this.saveButtonOptions)
          .onClick(async (event, result: SaveButtonOnClickResult) => {
            if (result == SaveButtonOnClickResult.SUCCESS) {
              try {
                let context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;
                let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
                // Ensure that the assets specified by imageFileUri and videoFileUri exist.
                let imageFileUri = 'file://' + context.resourceDir + '/hello.jpeg';
                let videoFileUri = 'file://' + context.resourceDir + '/sea.mp4';//加载的视频不要超过10秒

                let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =
                  photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(context,
                    photoAccessHelper.PhotoType.IMAGE, 'jpg', {
                      title: 'moving_photo',
                      subtype: photoAccessHelper.PhotoSubtype.MOVING_PHOTO
                    });

                assetChangeRequest.addResource(photoAccessHelper.ResourceType.IMAGE_RESOURCE, imageFileUri);
                assetChangeRequest.addResource(photoAccessHelper.ResourceType.VIDEO_RESOURCE, videoFileUri);

                await phAccessHelper.applyChanges(assetChangeRequest);

                this.statusMessage = 'create moving photo successfully, uri: ' + assetChangeRequest.getAsset().uri;
                console.info('create moving photo successfully, uri: ' + assetChangeRequest.getAsset().uri);
              } catch (err) {
                this.statusMessage = `create moving photo failed with error: ${err.code}, ${err.message}`;
                console.error(`create moving photo failed with error: ${err.code}, ${err.message}`);
              }
            } else {
              this.statusMessage = 'SaveButtonOnClickResult create moving photo failed';
              console.error('SaveButtonOnClickResult create moving photo failed');
            }
          })

        // ...

      }
      .width('100%')
      .height('100%')
    }
    .title('Save Moving Photo')
  }
}

1. 代码逻辑解析

代码片段使用了华为 photoAccessHelper API(基于 HarmonyOS 文档),核心流程如下:

// 1. 创建动态照片请求(类型为 IMAGE,子类型为 MOVING_PHOTO)
const changeRequest = photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(
  context,
  photoAccessHelper.PhotoType.IMAGE, // 资源类型:图片
  imageExtension,
  {
    title: util.generateRandomUUID(),
    subtype: photoAccessHelper.PhotoSubtype.MOVING_PHOTO // 动态照片类型
  }
);

// 2. 添加资源(关键步骤)
changeRequest.addResource(
  photoAccessHelper.ResourceType.IMAGE_RESOURCE, 
  { file: '/data/storage/el2/.../1779351671492.jpg' } // 图片资源路径
);
changeRequest.addResource(
  photoAccessHelper.ResourceType.VIDEO_RESOURCE, 
  { file: '/data/storage/el2/.../1779351671492.mp4' } // 视频资源路径
);

// 3. 提交请求(触发报错)
await photoAccessHelper.applyChanges(changeRequest);

2. 关键报错点

  • 错误码 401:在 HarmonyOS 中,401 通常表示 权限不足资源未通过安全校验
  • 错误信息Failed to check video resource of moving photo
    明确指向 视频资源检查失败,说明问题出在 VIDEO_RESOURCE 的处理环节。

3. 可能原因分析

可能原因 具体说明
权限缺失 未声明 ohos.permission.WRITE_MEDIAohos.permission.READ_MEDIA 权限,导致无法访问视频资源。
视频路径无效 file 路径指向的视频文件不存在、格式不支持(如非 MP4),或路径权限不足(如 el2 路径需沙箱访问)。
动态照片类型不匹配 MOVING_PHOTO 要求同时提供图片和视频资源,但视频资源可能不符合规范(如时长、分辨率)。
API 使用不合规 未按文档要求初始化 photoAccessHelper,或 context 未正确传递(如 Activity 上下文)。

4. 解决方案建议

步骤 1:检查权限声明

module.json5 中添加必要权限:

{
  "module": {
    "reqPermissions": [
      {
        "name": "ohos.permission.WRITE_MEDIA",
        "reason": "保存动态照片需要写入媒体库"
      },
      {
        "name": "ohos.permission.READ_MEDIA",
        "reason": "读取视频资源需要权限"
      }
    ]
  }
}
步骤 2:验证视频资源
  • 确保视频文件路径有效(通过 file API 检查文件是否存在)。
  • 确认视频格式为 MP4(HarmonyOS 动态照片通常要求 H.264 编码)。
  • 检查路径是否属于应用沙箱(如 context.filesDir 路径)。
步骤 3:参考官方文档规范
  • 动态照片需同时提供 图片(.jpg)视频(.mp4),且两者需为同一内容(如视频截图与视频匹配)。
  • 严格按文档示例构造 createAssetRequest 参数(官方文档链接)。
步骤 4:调试建议
  • addResource 后添加日志,确认视频文件路径是否可读:
console.log("Video file exists:", await file.exists(videoPath));
  • 捕获更详细的错误信息:
catch (err) {
  console.error("Detailed error:", JSON.stringify(err));
}

总结

该报错的核心是 视频资源校验失败,需优先排查 权限、路径有效性、视频格式 三方面问题。

401 错误在 HarmonyOS 中多与权限相关,建议先确保权限声明完整,并验证视频资源的合规性。

(注:动态照片功能需同时满足图片和视频的规范,缺一不可)

你好,错误码401可能权限不足,使用安全控件保存动态照片资源,无需申请相册管理模块权限’ohos.permission.WRITE_IMAGEVIDEO’,允许用户通过点击按钮临时获取存储权限,并将资源直接保存到指定的媒体库路径,使得操作更为便捷。详情请参考SaveButton

不要写死沙箱目录,而是根据context去获取沙箱路径,参考示例:

SaveButton(this.saveButtonOptions) 
  .onClick(async (event, result: SaveButtonOnClickResult) => {
    if (result == SaveButtonOnClickResult.SUCCESS) {
      try {
        let context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;
        let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
        // Ensure that the assets specified by imageFileUri and videoFileUri exist.
        let imageFileUri = 'file://' + context.filesDir + '/create_moving_photo.jpg';
        let videoFileUri = 'file://' + context.filesDir + '/create_moving_photo.mp4';
        ...
  })

这个报错:

401, Failed to check video resource of moving photo

本质上不是权限问题,

而是:

你传入的 mp4 不符合“动态照片(Moving Photo)”规范。

HarmonyOS 的:

PhotoSubtype.MOVING_PHOTO

并不是:

“随便一张 jpg + 一个 mp4”

拼起来就行。

系统会校验:

  • 视频编码
  • 时长
  • 分辨率
  • metadata
  • 图片与视频关联信息

如果不符合规范,

就会:

Failed to check video resource

最常见原因有这些:

1、mp4 不是系统支持的编码(最常见)

建议:

H264 + AAC

很多:

  • HEVC
  • AV1
  • 特殊编码

会失败。

2、视频时长不符合

Moving Photo 不是普通长视频。

一般要求:

  • 很短的视频
  • 通常几秒内

如果:

  • 视频太长
  • 空视频
  • 0帧视频

也会失败。

3、jpg 和 mp4 不匹配

动态照片要求:

  • 图片封面
  • 视频内容

属于同一拍摄资源。

如果:

  • 随便找个 jpg
  • 再随便找个 mp4

很多时候校验过不了。

4、mp4 文件路径不合法

你这里:

file://xxx.mp4

注意:

必须是真实可访问文件。

很多人:

  • 文件不存在
  • 还没 flush
  • 没有读权限
  • 临时文件被删

也会触发这个错误。

建议先验证:

fileIo.accessSync(path)

是否真存在。

5、不是“动态照片源文件”

这个特别关键。

HarmonyOS Moving Photo:

更偏向:

“系统动态照片格式”。

不是:

“普通图片 + 普通视频”。

所以:

最稳的方式是:

使用系统拍摄生成的动态照片资源。

而不是业务自己拼。

你这个场景里,

我怀疑最大概率是:

mp4 本身不符合 Moving Photo 规范。

建议这样排查:

先用系统相机拍一张:

动态照片

然后:

导出:

  • jpg
  • mp4

再替换你自己的资源测试。

如果系统资源能成功,

说明:

你的 mp4 格式有问题。

另外再注意:

图片和视频:

最好:

  • 同尺寸
  • 同拍摄比例
  • 同来源

不要:

64x64 jpg + 1920x1080 mp4

这种。

一句话总结:

HarmonyOS 的 Moving Photo 不是简单 jpg+mp4 拼接,系统会校验视频资源是否合法;你这个 401 大概率是 mp4 编码、时长、metadata 或资源匹配不符合动态照片规范导致的。

if (result == SaveButtonOnClickResult.SUCCESS) {
try {
let context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
// Ensure that the assets specified by imageFileUri and videoFileUri exist.
let imageFileUri = 'file://' + context.filesDir + '/create_moving_photo.jpg';
let videoFileUri = 'file://' + context.filesDir + '/create_moving_photo.mp4';
let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =
photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(context,
photoAccessHelper.PhotoType.IMAGE, 'jpg', {
title: 'moving_photo',
subtype: photoAccessHelper.PhotoSubtype.MOVING_PHOTO
});

官方实例大概也是这意思啊,

你好,开发建议:完全复制文档中的示例代码,准备资源文件上传到应用沙箱目录,仅替换jpg和mp4文件名,再验证是否可以保存动态图片。

看着像是文件路径有问题,打码的那段是注释了还是有前缀,建议用

getContext().filesDir

获取文件路径

HarmonyOS 鸿蒙Next中动态图片保存至图库报错,通常因未正确使用 PhotoAccessHelper API(如未传入有效 Uri 或未添加写权限 ohos.permission.WRITE_IMAGEVIDEO)。需确保动态图片格式为系统支持的 HEIF/PNG 序列,并使用 ImagePacker 或 Picture.createCopy 方法生成符合系统标准的资源。检查文件路径是否合法并调用 MediaAssetManager 进行保存。

报错“401, Failed to check video resource of moving photo”表明动态照片创建时无法校验视频资源。通常原因是传入的视频 URI 对应的文件不符合要求或不可访问。请检查以下几点:

  1. 视频格式:动态照片的视频部分要求 MP4 格式(H.264/H.265 编码),且时长需在 0.5 秒 ~ 3 秒之间(部分接口限制为 0.5~1.5 秒)。确认视频符合该约束。
  2. URI 有效性:确保 videoUri 指向应用沙箱内的已有文件(如通过临时路径创建并写入),且 未被删除或移动。若使用 file:/// 路径,请确认文件读写权限正常。
  3. 预备资源文件大小/参数:视频分辨率建议与图片适配,过高分辨率可能导致校验失败。尝试使用 1920×1080 或更低分辨率。
  4. URI 格式:需使用正确的 URI 格式,如通过 fileUri.getUriFromPath() 获取。避免传入媒体库 URI(media://)而应使用真实文件路径。
  5. 视频完整性:确保视频数据已完全写入并关闭文件句柄后,再调用 createMovingPhoto

若上述检查均无误,可尝试用官方示例中的视频参数重新生成视频片段,以排除视频编码问题。

回到顶部