在HarmonyOS 6上开发视频录制或编辑功能时,你是否遭遇过这种“灵异事件”:应用内显示视频“保存成功”,日志无任何报错,但打开系统图库却空空如也?你反复检查了ohos.permission.WRITE_USER_STORAGE权限,确认文件路径存在,甚至怀疑是手机存储Bug,但视频始终“只存不见”。

这并非系统Bug,而是HarmonyOS 6星盾安全架构下,“沙箱文件”与“系统媒体库”的彻底隔离。本文将彻底解析这一“图库隐身”现象,并提供一套基于MediaLibrarySaveButton的合规解决方案。

一、现象:保存成功,图库却“空空如也”

1. 问题现场:完美的保存,消失的视频

场景复现:用户录制或编辑完视频,点击“保存到相册”,应用控制台打印“保存成功”,文件路径为/data/storage/.../xxx.mp4(应用沙箱内路径)。但当用户打开系统图库或相册App时,视频根本不存在。

预期效果

实际效果

技术假象

视频写入路径 → 图库可见

❌ 视频写入沙箱 → 图库不可见

文件真实存在,但媒体库未索引

错误代码示例(导致“隐身”的元凶)

// ❌ 错误示例:直接写入沙箱路径(HarmonyOS 6 图库无法访问)
import fs from '@ohos.file.fs';

async function saveVideoToSandbox(videoData: ArrayBuffer) {
  let tempPath = getContext().filesDir + '/my_video.mp4'; // 沙箱私有路径
  let fd = fs.openSync(tempPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
  fs.writeSync(fd, videoData); // 写入成功
  fs.closeSync(fd);
  console.log('✅ 视频保存成功:', tempPath); // 但图库看不到!
}

2. 根因揭秘:星盾安全架构的“媒体库隔离”

核心机制:HarmonyOS 6 的星盾安全架构将应用沙箱与公共媒体库彻底隔离。应用沙箱内的文件(filesDir)对系统图库是“不可见”的,除非通过MediaLibraryAPI显式插入。

层级

文件位置

图库可见性

应用沙箱

/data/storage/.../files/video.mp4

❌ 不可见(私有)

公共媒体库

媒体库://video/xxx.mp4

✅ 可见(需显式插入)

失败本质:在HarmonyOS 6上,应用无法通过“直接写文件”的方式让视频出现在图库。必须通过MediaLibraryAPI将文件“注册”到系统媒体数据库,或使用SaveButton让用户主动选择保存位置。

二、解决方案:MediaLibrary显式插入 + SaveButton安全保存

1. 通道一:MediaLibrary 显式插入(推荐)

核心思路:先将视频写入沙箱临时文件,再通过MediaLibraryAPI将临时文件“移动”到公共媒体库,系统会自动索引并出现在图库。

修复代码

import mediaLibrary from '@ohos.file.mediaLibrary';
import fs from '@ohos.file.fs';

async function saveVideoToGallery(videoData: ArrayBuffer): Promise<void> {
  try {
    let context = getContext(this) as common.UIAbilityContext;
    let media = mediaLibrary.getMediaLibrary(context);

    // 1. 先写入沙箱临时文件
    let tempPath = context.filesDir + '/temp_video.mp4';
    let fd = fs.openSync(tempPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
    fs.writeSync(fd, videoData);
    fs.closeSync(fd);

    // 2. 通过MediaLibrary插入到公共媒体库
    let publicPath = await media.createAsset(
      mediaLibrary.MediaType.VIDEO,
      'my_video.mp4',
      mediaLibrary.DirectoryType.DIR_VIDEO
    );
    
    // 3. 将临时文件移动到公共位置(系统自动索引)
    await media.moveAsset(tempPath, publicPath);

    console.log('✅ 视频已插入图库:', publicPath);
  } catch (err) {
    console.error('❌ 保存失败:', err.message);
  }
}

2. 通道二:SaveButton 用户主动保存(合规)

适用场景:当需要用户确认保存位置或文件名时,使用SaveButton让用户主动选择,系统临时授予写入权限。

import fileShare from '@ohos.file.share';

@Entry
@Component
struct VideoEditPage {
  private tempVideoPath: string = ''; // 沙箱内临时视频路径

  build() {
    Column() {
      // 用户点击后系统弹出保存对话框
      SaveButton({
        fileList: [this.tempVideoPath],
        title: '保存旅行视频.mp4'
      })
        .onSaveSuccess((uri) => {
          console.log('✅ 视频已保存到相册:', uri);
          // 可选:删除沙箱临时文件
          fs.unlink(this.tempVideoPath);
        })
        .onSaveFailure((err) => {
          console.error('❌ 保存失败:', err.message);
        })
    }
  }
}

三、进阶:视频保存的“防呆”策略

1. 文件完整性校验(防“损坏”)

常见问题:视频文件未完全写入就被移动,导致图库显示“文件损坏”。

// 写入完成后校验文件大小
let stat = fs.statSync(tempPath);
if (stat.size === videoData.byteLength) {
  await media.moveAsset(tempPath, publicPath);
} else {
  throw new Error('视频文件写入不完整');
}

2. 避坑指南:视频保存的“三必须”

规则

原因

违反后果

必须使用MediaLibrary或SaveButton

沙箱文件对图库不可见

视频“隐身”

必须写入完成后再插入

媒体库索引需要完整文件

文件损坏

必须申请WRITE_USER_STORAGE权限

写入公共目录需用户授权

权限拒绝

四、总结:视频保存的“可见性”法则

  1. 沙箱是“黑盒”:应用沙箱内的文件,系统图库默认不可见

  2. 入库需“显式”:必须通过MediaLibraryAPI将文件插入媒体库,或通过SaveButton让用户主动保存。

  3. 路径不等于可见/data/storage/...路径的文件,永远不会自动出现在图库。

通过这套MediaLibrary+ SaveButton的“双通道”策略,你的视频保存功能将彻底告别“保存成功但图库没有”的幽灵状态,实现真正的所见即所得

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐