HarmonyOS运动开发:深度解析文件预览的正确姿势

##鸿蒙核心技术##运动开发##Core File Kit(文件基础服务)##Preview Kit(文件预览服务)#

在 HarmonyOS 开发中,文件预览功能是一个常见的需求,尤其是在处理用户上传的图片、文档、音频或视频等资源时。然而,实现文件预览并非一帆风顺,尤其是在涉及文件权限和预览窗口管理时。本文将以“选择好的文件进行预览”为主题,深入解析 HarmonyOS 中文件预览的核心要点,并分享一些实际开发中的经验技巧,帮助开发者避免常见的坑。

前言:文件预览的痛点与挑战

在 HarmonyOS 中,文件预览功能看似简单,实则暗藏诸多细节。开发者常常会遇到诸如文件权限不足、预览窗口无法正常打开或重复打开等问题。这些问题不仅影响用户体验,还可能导致应用崩溃或数据丢失。因此,掌握正确的文件预览实现方式至关重要。

核心要点:文件 URI 权限持久化

在 HarmonyOS 中,通过DocumentViewPicker拿到的文件 URI 仅具有临时权限,这种权限无法直接用于文件预览,否则会导致预览失败。因此,我们需要对文件 URI 进行持久化权限处理。

权限持久化代码解析

await fileShare.persistPermission([
  {
    uri: uri,
    operationMode: fileShare.OperationMode.READ_MODE
  }
]);

fileShare.persistPermission:这是关键的持久化方法,它将文件 URI 的权限从临时变为持久,确保预览功能能够正常访问文件。

uri:这是文件的路径,需要确保其格式正确。

operationMode:这里指定为READ_MODE,表示仅授予读取权限,这是预览功能所需要的最低权限。

此外,为了使用persistPermission方法,还需要在应用的config.json文件中声明以下权限:

{
  "name": "ohos.permission.FILE_ACCESS_PERSIST"
}

获取文件 MIME 类型

在预览文件之前,我们需要明确文件的 MIME 类型,这对于预览功能的正确性至关重要。以下是获取 MIME 类型的代码实现:

private getMimeType(filePath: string): string {
  const extension = filePath.split('.').pop()?.toLowerCase() || '';

  switch (extension) {
    case 'jpg':
    case 'jpeg':
      return 'image/jpeg';
    case 'png':
      return 'image/png';
    case 'gif':
      return 'image/gif';
    case 'bmp':
      return 'image/bmp';
    case 'webp':
      return 'image/webp';

    case 'mp4':
      return 'video/mp4';
    case 'mov':
      return 'video/quicktime';
    case 'avi':
      return 'video/x-msvideo';

    case 'mp3':
      return 'audio/mpeg';
    case 'wav':
      return 'audio/wav';
    case 'ogg':
      return 'audio/ogg';

    case 'txt':
    case 'log':
      return 'text/plain';

    case 'html':
    case 'htm':
      return 'text/html';

    default:
      return 'application/octet-stream';
  }
}

代码解析

filePath.split('.').pop():通过文件路径获取文件扩展名,这是判断 MIME 类型的关键。

switch语句:根据扩展名返回对应的 MIME 类型。这里涵盖了常见的图片、视频、音频、文本和 HTML 文件类型。

• 默认值:如果文件类型无法识别,则返回application/octet-stream,这是一种通用的二进制流类型。

预览文件的实现细节

预览文件的实现涉及多个步骤,包括检查文件是否存在、是否可预览、准备预览参数以及管理预览窗口。以下是完整的代码实现:

async previewFile(): Promise<void> {
  if (!this.selectedFilePath) {
    promptAction.showToast({ message: '请先选择文件', duration: 2000 });
    return;
  }

  try {
    let uiContext = this.getUIContext().getHostContext() as Context;

    // 1. 检查文件是否存在
    try {
      await fs.access(this.selectedFilePath);
    } catch {
      promptAction.showToast({ message: '文件不存在或不可访问', duration: 2000 });
      return;
    }

    // 2. 检查是否可预览
    const uri = this.selectedFilePath.startsWith('file://') ?
      this.selectedFilePath :
      `file://${this.selectedFilePath}`;

    await fileShare.persistPermission([
      {
        uri: uri,
        operationMode: fileShare.OperationMode.READ_MODE
      }
    ]);

    const canPreview = await filePreview.canPreview(uiContext, uri);
    if (!canPreview) {
      promptAction.showToast({ message: '不支持预览此文件类型', duration: 2000 });
      return;
    }

    // 3. 准备预览参数
    const fileInfo: filePreview.PreviewInfo = {
      title: this.fileName,
      uri: uri,
      mimeType: this.getMimeType(this.selectedFilePath)
    };

    // 4. 检查是否已有预览窗口
    const hasDisplayed = await filePreview.hasDisplayed(uiContext);

    if (hasDisplayed) {
        // 已有窗口则关闭
        await filePreview.closePreview(uiContext)
    } else {
      // 新开预览窗口
      const displayInfo: filePreview.DisplayInfo = {
        x: 100,  // 窗口起始x坐标
        y: 100,  // 窗口起始y坐标
        width: 800, // 窗口宽度
        height: 800 // 窗口高度
      };

      await filePreview.openPreview(uiContext, fileInfo, displayInfo);
    }

    console.info('文件预览成功');
  } catch (err) {
    const error = err as BusinessError;
    console.error(`预览失败,错误码: ${error.code}, 错误信息: ${error.message}`);
    promptAction.showToast({
      message: `预览失败: ${error.message}`,
      duration: 2000
    });
  }
}

代码解析

• 检查文件是否存在:通过fs.access方法检查文件路径是否有效。

• 持久化文件权限:使用fileShare.persistPermission方法确保文件 URI 具有持久化读取权限。

• 检查是否可预览:调用filePreview.canPreview方法判断文件类型是否支持预览。

• 准备预览参数:构建filePreview.PreviewInfo对象,包含文件标题、URI 和 MIME 类型。

• 管理预览窗口:通过filePreview.hasDisplayed检查是否已有预览窗口。如果存在,则调用filePreview.closePreview关闭窗口;如果不存在,则调用filePreview.openPreview新开预览窗口。

总结:文件预览的正确实现之道

在 HarmonyOS 开发中,实现文件预览功能需要关注文件权限、MIME 类型获取以及预览窗口管理等多个方面。

全部评论

相关推荐

面试官全程关摄像头1.自我介绍一下2.React和Vue哪个更熟悉一点3.你在之前那段实习经历中有没有什么技术性的突破(我只是实习了44天工作28天,我把我能说的都说了)4.你封装的哪个表单组件支不支持动态传值5.自己在实习阶段Vue3项目封装过hook吗6.hook有什么作用7.Vue2和Vue3的响应式区别(我说一个是proxy是拦截所有的底层操作,Object.defineProperty本身就是一个底层操作,有些东西拦截不了,比如数组的一些操作还有等等,面试官就说实在要拦截能不能拦截????我心想肯定不行呀,他的底层机制就不允许吧)8.pinia和vuex的区别(这个回答不出来是我太久没用了)9.pinia和zustand的区别,怎么选(直接给我干懵了)(我说react能用pinia吗&nbsp;&nbsp;他说要用的话也可以)10.渲染一万条数据,怎么解决页面卡顿问题(我说分页、监听滚轮动态加载,纯数据展示好像还可以用canvas画)(估计是没说虚拟表单,感觉不满意)11.type和interface的区别12.ts的泛型有哪些作用(我就说了一个结构相同但是类型不同的时候可以用,比如请求响应的接口,每次的data不同,这里能用一个泛型,他问我还有什么)13.你项目用的是React,如果让你再写一遍你会选择什么14.pnpm、npm、yarn的区别15.dependencies和devdependencies的区别总而言之太久没面试了,上一段实习的面试js问了很多。结果这次js一点没问,网络方面也没考,表现得很一般,但是知道自己的问题了&nbsp;&nbsp;好好准备,等待明天的影石360和周四的腾讯了&nbsp;&nbsp;加油!!!
解zj:大三的第一段面试居然是这样的结局
查看15道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务