HarmonyOS 文件预览服务避坑指南:从入门到真香

一、背景引入:这玩意儿是干啥的?

咱今天聊的这个 Preview Kit,中文名儿叫"文件预览服务"。听名字就知道,这玩意儿就是帮你预览文件的。

你可能会问:“预览文件?我自己写个组件不就完了吗?”

嘿,您要真这么想,那我得给您点个赞——有这股劲儿,当年我写代码也是这么想的。但踩了几个坑之后,我就服了。

为啥要用 Preview Kit?

咱说个实际场景:

你在应用里做了个文件管理器,用户点了个 Word 文档,你得让人家看到内容吧?这时候你有几个选择:

  1. 自己写解析器:docx、xlsx、pptx、pdf… 您慢慢写,写完了叫我一声
  2. 接第三方 SDK:WPS、OnlyOffice,选一个,然后掏钱
  3. 用 Preview Kit:系统自带的,香

我选 3,为啥?

  • 不用自己解析:Office 文档、PDF、图片、视频、音频,系统都给你整明白了
  • 不用额外花钱:WPS 那 SDK 可不便宜
  • 用户体验好:右上角还能"用其他应用打开",跳转原生应用,多顺滑

说白了,Preview Kit 就是个"神级 API",你给它一个文件 URI,它给你弹个预览窗口,完事儿。

这玩意儿能预览啥?

咱列个表,您心里有数:

类型支持的后缀备注
图片jpg、png、gif、webp、bmp、svg这个最基础,肯定支持
视频mp4、mkv、ts常见格式都 OK
音频m4a、aac、mp3、ogg、wav听个响没问题
文本txt、cpp、c、h、java、xml代码文件也能看
网页html、htm内嵌网页预览
PDFpdf这个用得最多
Officedoc、docx、xls、xlsx、ppt、pptx、csv、ofd借助 WPS 能力

注意啊:Office 文档预览是借助 WPS 的能力,所以预览界面会有 WPS 的技术支持标识,这个你别想着去掉,人家出的力。


二、整体架构:大概长啥样?

Preview Kit 的架构,说白了就三层:

┌─────────────────────────────────────────┐ │ 你的应用 │ (调用 openPreview 接口) └─────────────────┬───────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Preview Kit 服务 │ (解析文件 + 渲染预览界面) └─────────────────┬───────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 系统能力/WPS │ (真正的解析和渲染) └─────────────────────────────────────────┘ 

工作流程

  1. 你调用 openPreview,传入文件 URI
  2. Preview Kit 检查文件类型,决定咋预览
  3. 图片视频音频:系统直接渲染
  4. Office/PDF:调用 WPS 能力
  5. 弹出一个模态窗,显示预览内容
  6. 用户右上角点"用其他应用打开",跳转原生应用

几个概念先整明白

  • 模态窗:就是那个预览窗口,它出来之后,你爹窗口就动不了了,只能操作它
  • 应用窗:你自己的应用窗口
  • AMS:AbilityManagerService,系统服务,管窗口调度的

三、核心功能:咋整?

3.1 最基础的预览

来,上代码,最简单的预览:

import{ preview }from'@kit.PreviewKit';// 假设你有个文件 URIlet fileUri ="file://com.example.app/files/test.docx";// 直接预览 preview.openPreview({ uri: fileUri }).then(()=>{console.info("预览打开成功");}).catch((err)=>{console.error("预览打开失败", err);});

就这?就这!

对,就这么简单。你给它 URI,它给你预览,完事儿。

3.2 判断文件能不能预览

不是所有文件都能预览的,你得先判断一下:

import{ preview }from'@kit.PreviewKit';let fileUri ="file://com.example.app/files/test.xxx";// 先判断能不能预览 preview.canPreview({ uri: fileUri }).then((result)=>{if(result){// 能预览,打开 preview.openPreview({ uri: fileUri });}else{// 不能预览,提示用户 promptAction.showToast({ message:"这文件预览不了,您换个应用打开吧"});}});

建议:这个 canPreview 最好加上,别到时候用户点了个不支持的文件,直接报错,体验不好。

3.3 文件打开加速:这玩意儿有点东西

从 HarmonyOS 5.0.3(15) 开始,多了个"文件打开加速"功能。

啥意思呢?

你有个 10MB 的 Word 文档,用户点开后,通常得等个几秒甚至十几秒才能打开。文件打开加速就是提前预测用户可能要打开哪个文件,提前在后台预加载,等用户真点的时候,"唰"一下就开了。

爽感体验,懂吧?

咋接入?

这玩意儿是 C/C++ 的 API,ArkTS 暂时还没有。你得这么整:

步骤 1:申请权限

module.json5 里加:

"requestPermissions": [ { "name": "ohos.permission.PRELOAD_FILE" } ] 

注意ohos.permission.PRELOAD_FILE 是受限权限,你得去 AGC 申请,不是想用就能用的。

步骤 2:CMakeLists.txt 加依赖

target_link_libraries(entry PUBLIC libopen_file_boost.so) 

步骤 3:注册回调

#include"PreviewKit/open_file_boost.h"// 查询应用状态的回调 OpenFileBoost_AppState AppQueryAppStateCb(void){// 如果当前允许预加载,返回 ALLOWif(CanPreload()){return OPEN_FILE_BOOST_APP_STATE_ALLOW_PRELOAD;}else{return OPEN_FILE_BOOST_APP_STATE_REJECT_PRELOAD;}}// 预加载回调 OpenFileBoost_CbErrCode AppOnFilePreload(void* fileInfo){int32_t fileDescriptor =0;// 获取文件描述符HMS_OpenFileBoost_GetFdFromPreloadFileInfo(fileInfo,&fileDescriptor);// 获取文件路径char sandboxPath[MAX_BUFFER_LENGTH]={0};HMS_OpenFileBoost_GetSandboxPathFromPreloadFileInfo( fileInfo, sandboxPath, MAX_BUFFER_LENGTH );// 这里你自己整预加载逻辑// 比如把文件内容先读到内存里return OPEN_FILE_BOOST_CALLBACK_SUCCESS;}// 取消预加载回调 OpenFileBoost_CbErrCode AppCancelFilePreload(void* fileInfo){// 系统说"哎刚才那个文件不用预加载了"// 你在这儿清理资源return OPEN_FILE_BOOST_CALLBACK_SUCCESS;}// 注册 OpenFileBoost_ErrCode ret =HMS_OpenFileBoost_RegisterFilePreload( AppQueryAppStateCb, AppOnFilePreload, AppCancelFilePreload );if(ret != OPEN_FILE_BOOST_SUCCESS){// 注册失败,处理一下}

步骤 4:用户打开文件时通知系统

// 用户真打开了预加载的文件HMS_OpenFileBoost_NotifyPreloadHit( fileDescriptor, sandboxPath, pathLen );

这步很重要,你通知了系统,系统才知道"哦这文件预加载命中了",下次预测更准。

3.4 预加载状态感知:UI 提示

从 5.0.5(17) 开始,还能感知预加载状态,在 UI 上给用户提示。

场景:你做个下载管理器,文件下载完了,系统说"这文件我正在预加载",你可以在 UI 上显示个小动画,用户就知道"哦系统在加速"。

import{ openFileBoost }from'@kit.PreviewKit';// 1. 注册状态回调 openFileBoost.on('filePreloadStateChanged',(info)=>{if(info.state === openFileBoost.FilePreloadState.PRELOADING){// 预加载中,显示 loading 动画showLoadingAnimation();}if(info.state === openFileBoost.FilePreloadState.PRELOADED){// 预加载完成,显示完成动画showLoadedAnimation();}if(info.state === openFileBoost.FilePreloadState.NOT_PRELOADED){// 没预加载,啥也别显示}});// 2. 添加要监听的文件 openFileBoost.addFile("/storage/Users/currentUser/Desktop/10MB_file.docx");// 3. 查询当前状态let status = openFileBoost.queryFilePreloadStatusInfo("/storage/Users/currentUser/Desktop/10MB_file.docx");console.info("预加载状态:", status.state);

注意

  • 最多监听 50 个文件
  • 不用的时候 removeFile 取消监听
  • 退出的时候 off 注销回调

四、避坑指南

好了,重头戏来了。下面是我踩过的坑,你们别踩。

坑 1:传入路径而不是 URI

翻车现场

// 错误示范 preview.openPreview({ uri:"/storage/Users/currentUser/Desktop/test.docx"// 这是路径!});// 结果:预览失败,没报错,就是打不开

正确姿势

// 正确示范 preview.openPreview({ uri:"file://com.example.app/files/test.docx"// 这是 URI});

记住openPreview 只认 URI,不认路径。URI 长这样:file:// 开头。

坑 2:DocumentViewPicker 的 URI 没持久化权限

翻车现场

// 用户通过 DocumentViewPicker 选了个文件let filePicker =newdocumentViewPicker.DocumentViewPicker();let uris =await filePicker.open();// 直接预览 preview.openPreview({ uri: uris[0]});// 结果:预览失败,提示"无权限"

为啥?

DocumentViewPicker 拿到的 URI,你只有临时权限,这权限不能分享给预览服务。

正确姿势

// 方式 1:持久化权限import{ fileUri }from'@kit.CoreFileKit';let uris =await filePicker.open();// 先持久化权限 fileUri.acquirePersistedUriPermission(uris[0]);// 再预览 preview.openPreview({ uri: uris[0]});// 方式 2:拷贝到沙箱import{ fileIo }from'@kit.CoreFileKit';let uris =await filePicker.open();// 拷贝到沙箱let sandboxUri ="file://com.example.app/files/copied.docx";await fileIo.copy(uris[0], sandboxUri);// 预览沙箱文件 preview.openPreview({ uri: sandboxUri });

建议:用方式 2,简单,不用操心权限问题。

坑 3:模拟器上测 Office 预览

翻车现场

// 模拟器上 preview.openPreview({ uri:"file://.../test.docx"});// 结果:打不开,以为是代码问题

真相

模拟器不支持 .pdf.pptx.xlsx.docx 等文档类文件的预览。真机可以,模拟器不行。

正确姿势

别在模拟器上测文档预览,直接上真机。模拟器只测测图片视频得了。

坑 4:文件打开加速只在 2in1 设备上支持

翻车现场

// 在手机上测试文件打开加速// 结果:注册回调没反应,以为代码写错了

真相

文件打开加速功能只在 2in1 设备(就是那种能当平板又能当电脑的设备)上支持。手机、平板都不支持。

正确姿势

// 先检查系统能力import{ systemCapability }from'@kit.SystemCapabilityKit';let supported = systemCapability.isSystemCapabilityAvailable('SystemCapability.PCService.OpenFileBoost');if(supported){// 支持,注册加速registerFilePreload();}else{// 不支持,别费劲了console.info("这设备不支持文件打开加速");}

坑 5:受限权限没申请

翻车现场

// module.json5 "requestPermissions": [ { "name": "ohos.permission.PRELOAD_FILE" } ] 

代码写好了,一运行,报错"权限不足"。

真相

ohos.permission.PRELOAD_FILE 是受限权限,不是你在 module.json5 里写了就行的。你得去 AGC(AppGallery Connect)申请,提交材料,等审核。

正确姿势

  1. 先去 AGC 申请受限权限
  2. 等审核通过
  3. 下载权限证书
  4. 配置到项目里
  5. 然后再用

建议:如果只是为了预览,别整文件打开加速,那玩意儿门槛高。基础的 openPreview 就够了。

坑 6:预览完不关窗口

翻车现场

用户点预览,预览窗口出来了,看完想关掉,找不到关闭按钮。

真相

Preview Kit 的预览窗口是模态窗,右上角有个"×",但有些用户找不到。

正确姿势

这个真没法改,系统设计的。但你可以在预览之前提示用户:

promptAction.showDialog({ title:"提示", message:"预览窗口右上角可以关闭", buttons:[{ text:"知道了"}]});

坑 7:URI 不存在

翻车现场

// 文件已经被删了,但 URI 还在 preview.openPreview({ uri:"file://.../deleted.docx"});// 结果:没反应,也不报错

正确姿势

import{ fileIo }from'@kit.CoreFileKit';asyncfunctionsafePreview(uri:string){// 先检查文件存在不try{await fileIo.access(uri);// 存在,预览 preview.openPreview({ uri });}catch{// 不存在,提示 promptAction.showToast({ message:"文件不存在,可能已经被删除了"});}}

五、完整实战案例

来,整个完整的例子,你直接抄作业。

场景:文件管理器预览功能

// FilePreviewManager.tsimport{ preview }from'@kit.PreviewKit';import{ fileIo }from'@kit.CoreFileKit';import{ promptAction }from'@kit.ArkUI';exportclassFilePreviewManager{// 预览文件asyncpreviewFile(uri:string):Promise<void>{// 1. 检查文件存在try{await fileIo.access(uri);}catch{ promptAction.showToast({ message:"文件不存在"});return;}// 2. 检查能不能预览try{let canPreview =await preview.canPreview({ uri });if(!canPreview){ promptAction.showToast({ message:"这文件预览不了,用其他应用打开吧"});return;}}catch{ promptAction.showToast({ message:"预览服务不可用"});return;}// 3. 打开预览try{await preview.openPreview({ uri });console.info("预览打开成功");}catch(err){console.error("预览打开失败", err); promptAction.showToast({ message:"预览打开失败,请稍后重试"});}}// 批量预览(比如用户选了多个图片)asyncpreviewFiles(uris:string[]):Promise<void>{for(let uri of uris){awaitthis.previewFile(uri);// 稍微等会儿,别一下全弹出来awaitnewPromise(resolve =>setTimeout(resolve,300));}}}// 使用let previewManager =newFilePreviewManager();// 用户点了个文件 previewManager.previewFile("file://com.example.app/files/test.docx");

六、总结

好了,唠了这么多,总结一下:

Preview Kit 能干啥?

  • 预览图片、视频、音频、文本、HTML、PDF、Office 文档
  • 右上角能"用其他应用打开",跳转原生应用
  • 可选的文件打开加速功能(2in1 设备)

咋用?

import{ preview }from'@kit.PreviewKit'; preview.openPreview({ uri:"file://..."});

就这,简单。

有啥坑?

  1. 只认 URI,不认路径
  2. DocumentViewPicker 的 URI 要持久化权限或拷贝到沙箱
  3. 模拟器不支持文档预览
  4. 文件打开加速只在 2in1 设备上支持
  5. 受限权限要去 AGC 申请
  6. 先检查文件存在不

我的建议

如果你就是做个简单的预览功能:

  • openPreview 就够了
  • 别整文件打开加速,那玩意儿门槛高
  • 记得加 canPreview 判断
  • URI 一定要检查存在不

如果你要做深度优化:

  • 文件打开加速可以考虑,但只针对 2in1 设备
  • 预加载状态感知可以加,UI 提示更友好
  • 受限权限提前申请,别等上线了才发现用不了

Read more

Docker Desktop for Mac 历史版本下载大全(macOS 10.15/11/12)

Docker Desktop for Mac 历史版本下载大全(macOS 10.15/11/12)

Docker Desktop for Mac 历史版本下载大全(macOS 10.15/11/12) 本文整理收集了各版本 macOS 系统对应的 Docker Desktop 历史版本下载链接,方便需要特定版本的用户下载使用。 各 macOS 版本对应的 Docker Desktop 最终支持版本 🍎 macOS Catalina (10.15) 最后一个支持版本 版本号:v4.15.0 下载链接: * Intel 芯片:https://desktop.docker.com/mac/main/amd64/93002/Docker.dmg 🍎 macOS Big Sur (11.x)

By Ne0inhk
Flutter 组件 test_reflective_loader 适配鸿蒙 HarmonyOS 实战:反射装载矩阵,构建规模化测试的自动化分发中枢

Flutter 组件 test_reflective_loader 适配鸿蒙 HarmonyOS 实战:反射装载矩阵,构建规模化测试的自动化分发中枢

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 test_reflective_loader 适配鸿蒙 HarmonyOS 实战:反射装载矩阵,构建规模化测试的自动化分发中枢 前言 在鸿蒙(OpenHarmony)生态迈向大规模企业级应用、涉及深度组件解耦与多维功能验证的背景下,如何通过标准化的框架降低测试样板代码(Boilerplate)的维护成本,已成为决定项目迭代质效的“深水区工程”。在鸿蒙设备这类强调 AOT 编译性能与严苛环境隔离的移动终端上,如果依然依赖传统的手工挂载单元测试用例,由于由于随着业务规模膨胀而呈几何级增长的维护量,极易由于由于人为疏漏导致核心路径的测试脱节。 我们需要一种能够在开发期利用反射特性自动探测用例、支持面向对象继承复用且具备高度声明式语义的测试装载方案。 test_reflective_loader 为 Flutter 开发者引入了基于反射的测试组织范式。它允许通过定义标准的测试类(Test Classes),并在运行时自动识别带有特定前缀的测试

By Ne0inhk
Linux 源配置不用慌!CentOS/Ubuntu 源更新(含恢复)+Yum 操作 + Vim 入门,新手也能懂!

Linux 源配置不用慌!CentOS/Ubuntu 源更新(含恢复)+Yum 操作 + Vim 入门,新手也能懂!

✨ 孤廖:个人主页 🎯 个人专栏:《C++:从代码到机器》 🎯 个人专栏:《Linux系统探幽:从入门到内核》 🎯 个人专栏:《算法磨剑:用C++思考的艺术》 折而不挠,中不为下 文章目录 * 前言: * 正文: * 软件包管理器 * 1.什么是软件包 * 2. Linux软件⽣态 * 3. yum具体操作 * 查看软件包 * 安装软件 * 卸载软件 * Centos7更新yum源 * 1. 备份现有Yum源 * 2. 下载新的Yum源配置⽂件 * 3. 清理并⽣成缓存 * 4. 更新系统 * 5. 恢复原有Yum源(可选) * 6. 验证Yum源 * Ubuntu 更新apt源 * 1. 备份现有APT源 * 2. 直接下载新的APT源配置⽂

By Ne0inhk
Flutter 三方库 a2a 的鸿蒙化适配指南 - 实现高效的 Array-to-Array 结构转换、支持跨维度数据映射与集合内容深度克隆

Flutter 三方库 a2a 的鸿蒙化适配指南 - 实现高效的 Array-to-Array 结构转换、支持跨维度数据映射与集合内容深度克隆

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 a2a 的鸿蒙化适配指南 - 实现高效的 Array-to-Array 结构转换、支持跨维度数据映射与集合内容深度克隆 前言 在进行 Flutter for OpenHarmony 的大规模数据处理或图形计算开发时,经常需要对多维数组(嵌套列表)进行结构化调整。例如,将一个扁平化的传感器采样序列转换为 UI 渲染所需的网格坐标点集。a2a 是一个专门为 Array-to-Array 转换设计的极简工具库。它致力于通过声明式的 API 解决集合变换过程中的逻辑繁琐问题。本文将探讨如何在鸿蒙端利用该库提升集合操作的优雅度。 一、原原理性解析 / 概念介绍 1.1 基础原理 a2a 建立在一套强大的“映射算子(Mapping Operators)”之上。它获取输入数组,通过定义的投影(Project)

By Ne0inhk