鸿蒙Preview预览文件失败原因

特别注意:关键避坑点

要在鸿蒙应用中预览沙箱路径下的 .docx 文件,核心是使用 PreviewKit 并严格遵循 URI 格式、权限配置和 MIME 类型规范,以下是可直接运行的完整代码和避坑指南(适配 API 10+,HarmonyOS 5.0+)

一、前置配置(必须完成)

1. 权限声明(module.json5

需要申请文件读写权限,并引入 PreviewKit 模块:

复制代码
{
  "module": {
    // 权限声明
    "requestPermissions": [
      { "name": "ohos.permission.READ_USER_STORAGE" },
      { "name": "ohos.permission.WRITE_USER_STORAGE" }
    ],
    // 引入预览工具包
    "usesModules": [
      { "name": "@kit.PreviewKit" }
    ]
  }
}
2. 动态申请权限(可选,高版本鸿蒙需动态授权)

在页面初始化时申请存储权限,避免预览时因权限不足失败:

复制代码
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import bundleManager from '@ohos.bundle.bundleManager';

// 权限申请函数
async requestStoragePermission() {
  const atManager = abilityAccessCtrl.createAtManager();
  const permissions = ['ohos.permission.READ_USER_STORAGE'];
  try {
    const grantStatus = await atManager.checkAccessToken(
      abilityAccessCtrl.createTokenID(
        this.uiContext.abilityInfo.bundleName,
        this.uiContext.abilityInfo.applicationInfo.uid
      ),
      permissions[0]
    );
    if (grantStatus !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
      // 申请权限
      await atManager.requestPermissionsFromUser(this.uiContext, permissions);
    }
  } catch (err) {
    console.error('权限申请失败:', err);
  }
}

二、完整 ArkTS 实现代码

复制代码
import { filePreview } from '@kit.PreviewKit';
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import bundleManager from '@ohos.bundle.bundleManager';

@Entry
@Component
struct DocxPreviewPage {
  // 获取 UI 上下文
  private uiContext = getContext(this) as common.UIAbilityContext;
  // 目标文件名称
  private docFileName = '关于 2023 年元旦及春节假期安排的预通知.docx';
  // 沙箱内文件绝对路径
  private docFilePath = `/data/storage/el2/base/haps/entry/files/${this.docFileName}`;

  build() {
    Column({ space: 20, alignItems: ItemAlign.Center })
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center) {
        Button('预览 Word 文档')
          .width(200)
          .height(50)
          .onClick(async () => {
            // 先申请权限
            await this.requestStoragePermission();
            // 执行预览
            this.previewDocxFile();
          })
      }
  }

  // 核心预览方法
  private previewDocxFile() {
    // 1. 校验文件是否存在
    if (!fs.accessSync(this.docFilePath)) {
      console.error(`文件不存在:${this.docFilePath}`);
      return;
    }

    // 2. 构造 标准 URI(关键!必须带应用包名)
    const appBundleName = this.uiContext.abilityInfo.bundleName;
    const fileUri = `file://${appBundleName}${this.docFilePath}`;

    // 3. 配置预览参数
    const previewParam: filePreview.PreviewInfo = {
      title: this.docFileName, // 预览窗口标题
      uri: fileUri,            // 带包名的文件 URI
      // docx 格式的标准 MIME 类型(不可写错)
      mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    };

    // 4. 调用系统预览接口
    filePreview.openPreview(this.uiContext, previewParam)
      .then(() => console.info('预览启动成功'))
      .catch((err: BusinessError) => {
        console.error(`预览失败:错误码=${err.code},原因=${err.message}`);
      });
  }

  // 动态申请存储权限
  private async requestStoragePermission() {
    const atManager = abilityAccessCtrl.createAtManager();
    const permissions = ['ohos.permission.READ_USER_STORAGE'];
    try {
      const tokenId = abilityAccessCtrl.createTokenID(
        this.uiContext.abilityInfo.bundleName,
        this.uiContext.abilityInfo.applicationInfo.uid
      );
      const grantStatus = await atManager.checkAccessToken(tokenId, permissions[0]);
      if (grantStatus !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
        await atManager.requestPermissionsFromUser(this.uiContext, permissions);
      }
    } catch (err) {
      console.error('权限申请异常:', err);
    }
  }
}

三、关键避坑点(90% 失败原因在这里)

1.URI 格式必须正确
  • ❌ 错误写法:file:///data/storage/el2/...(缺少应用包名)
  • ✅ 正确写法:file://{应用包名}/data/storage/el2/...
  • 动态获取包名:this.uiContext.abilityInfo.bundleName,避免硬编码。
2.MIME 类型不能混淆

|-------|-------------------------------------------------------------------------|----------------------------------|
| 文件类型 | 正确 MIME 类型 | 错误 MIME 类型 |
| .docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document | application/msword(这是 .doc 格式) |

3.文件路径校验
  • fs.accessSync(filePath) 提前判断文件是否存在,避免因文件未拷贝到沙箱路径导致失败。
  • 若文件是从外部导入,需先通过 fileIo 将文件写入 entry/files 目录。
4.低版本兼容(API < 10)

低版本鸿蒙无 PreviewKit,需通过 隐式 Want 调用 WPS 等第三方应用打开:

复制代码
private openByThirdApp() {
  const want = {
    action: 'ohos.want.action.viewData',
    uri: this.fileUri,
    type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  };
  this.uiContext.startAbility(want).catch(err => console.error('调用第三方失败:', err));
}

四、常见问题排查

|---------------|----------------------------------------|
| 问题现象 | 排查方向 |
| 预览无响应,错误码 -1 | 检查 URI 是否带应用包名 |
| 提示 "不支持的文件格式" | 核对 MIME 类型是否为 docx 专属 |
| 提示 "权限不足" | 检查 module.json5 权限声明 + 动态申请权限 |
| 提示 "文件不存在" | 确认文件已放入 entry/files 目录,路径拼写无空格 / 错别字 |

相关推荐
三声三视10 小时前
ArkTS 性能优化实战:从卡顿分析到高帧率应用全攻略
华为·性能优化·harmonyos·鸿蒙
小雨青年11 小时前
鸿蒙 HarmonyOS 6 | PDFKit预览能力升级实战
华为·harmonyos
花先锋队长12 小时前
鸿蒙6.1加持菜鸟App:地理围栏+实况窗,靠近驿站自动提醒,取件不再遗漏
华为·智能手机·harmonyos
nashane12 小时前
HarmonyOS 6学习:页面跳转弹窗状态保持全解析
学习·华为·harmonyos·harmonyos 5
maaath13 小时前
【maaath】Flutter for OpenHarmony 实战:电影榜单应用开发指南
flutter·华为·harmonyos
若兰幽竹14 小时前
【HarmonyOS 6.1 全场景实战】开篇词:打造消除“吃饭焦虑”的《灵犀厨房》
harmonyos·鸿蒙开发·华为鸿蒙系统
机构师15 小时前
<鸿蒙><APP><3D>鸿蒙3D开发,如何获取ktx格式的天空盒图?
华为·harmonyos
xmdy586616 小时前
Flutter+开源鸿蒙实战|智安盾电商溯源平台Day6 登录逻辑+积分体系+全局收尾优化
flutter·华为·harmonyos
前端不太难16 小时前
AISystem:鸿蒙游戏中的 AI 行为驱动
人工智能·游戏·harmonyos
xmdy586618 小时前
Flutter+开源鸿蒙实战|智联邻里Day1 项目搭建+环境适配+架构规划(十五五民生创新版)
flutter·开源·harmonyos