写点什么

HarmonyOS preview 预览文件 Kit 的入门讲解

作者:万少
  • 2025-11-13
    广东
  • 本文字数:3478 字

    阅读完需:约 11 分钟

HarmonyOS preview 预览文件 Kit 的入门讲解

本文以实际工程为例,快速上手 HarmonyOS 元服务 的文件预览能力(PreviewKit),并配套一个后端用于提供示例文件。示例工程路径:

  • 客户端(HarmonyOS 端):client

  • 后端(Node.js):server

image-20251112090708795



image-20251112091151694

上图是将 1 个 pdf 文件和 3 个图片一起预览,那么就只会现实第 1 个预览窗口。

下图是移除 pdf 文件,将 3 个同类型的图片放在一起预览

image-20251112091518239


为了方便演示功能,需要先将一些可以预览的文件下载到元服务的沙箱内,是基于这个原因我们才需要引入后端来模拟这个下载的环境,所以元服务内需要先实现下载文件,存储到沙箱,然后再使用预览 API filePreview.openPreview 预览沙箱内的文件。

1. 工程结构与目标

  • client/entry/src/main/ets/pages/Index.ets:演示并发下载 4 个文件(1.pdf1.png2.png3.png)并一次性预览。

  • server/index.js 与 server/public/:提供静态文件下载接口 /file/:filename

目标:

  • 点击“下载”按钮,并发下载上述 4 个文件到应用沙箱目录。

  • 下载成功后点击“预览”,一次性打开最多 4 个文件的预览窗口。


2. PreviewKit 的核心:filePreview.openPreview

HarmonyOS 提供了预览能力包 @kit.PreviewKit。在 ETS 代码中引入:

import { filePreview } from '@kit.PreviewKit';import { fileUri } from '@kit.CoreFileKit';
复制代码

核心调用是:

// 先准备多个文件的预览信息const prewList: filePreview.PreviewInfo[] = []for (let i = 0; i < count; i++) {  const item = this.lastDownloadedList[i];  const fileInfo: filePreview.PreviewInfo = {    title: item.name,                                  // 预览标题    uri: fileUri.getUriFromPath(item.path),            // 将沙箱路径转成 Uri    mimeType: item.mime || 'application/octet-stream', // MIME 类型  };  prewList.push(fileInfo)}
// 一次性打开多个预览窗口filePreview.openPreview(uiContext, prewList)  .then(() => {    // 打开成功  })  .catch((err: BusinessError) => {    // 打开失败处理  });
复制代码

说明:

  • PreviewInfo 至少需要 titleurimimeType

  • uri 使用 fileUri.getUriFromPath(沙箱文件路径) 构造。

  • 支持一次性传入一个 PreviewInfo[],实现多文件预览。

图片占位:请补充一次性预览 4 个文件的窗口布局截图,标注窗口标题与 MIME 类型展示位置。


3. 并发下载与状态反馈(客户端)

示例使用 Promise.allSettled 并发下载 4 个后端文件,并按项展示“成功/失败”状态:

// 计划 + 状态@Local private plannedFiles: DownloadPlan[] = [];@Local private itemStatuses: string[] = [];@Local private isDownloading: boolean = false;@Local private statusMessage: string = '';
// 初始化计划(aboutToAppear)this.plannedFiles = [  new DownloadPlan('1.pdf', `${this.serverBase}/1.pdf`),  new DownloadPlan('1.png', `${this.serverBase}/1.png`),  new DownloadPlan('2.png', `${this.serverBase}/2.png`),  new DownloadPlan('3.png', `${this.serverBase}/3.png`)];this.itemStatuses = ['未下载','未下载','未下载','未下载'];
// 点击“下载”this.isDownloading = true;this.statusMessage = '下载中...';this.itemStatuses = new Array(this.plannedFiles.length).fill('下载中...');
const promises: Promise<DownloadInfo>[] = this.plannedFiles.map(p => this.downloadFile(p.url));const settled = await Promise.allSettled(promises);
// 汇总结果并一次性触发 UI 刷新const successes: DownloadInfo[] = [];const nextStatuses: string[] = new Array(this.plannedFiles.length).fill('未下载');for (let i = 0; i < settled.length; i++) {  const name = this.plannedFiles[i].name;  const r = settled[i];  if (r.status === 'fulfilled') {    successes.push(r.value);    nextStatuses[i] = `✓ 下载成功:${name}`;  } else {    nextStatuses[i] = `✗ 下载失败:${name}(${this.errorToString(r.reason as Object)})`;  }}this.itemStatuses = nextStatuses; // 重新赋值以触发 UI 刷新this.lastDownloadedList = successes;this.isDownloading = false;
复制代码

UI 渲染建议:

  • 使用 ForEach(this.plannedFiles, ...) 动态渲染状态行,避免硬编码索引。

  • 将与 UI 绑定的字段用 @Local 或 @State 修饰,并“重新赋值数组”以触发刷新(不要在原数组上就地修改元素)。

图片占位:请补充“下载中→成功/失败”逐项状态变化的截图,便于读者理解响应式刷新。


4. HTTP 下载的细节与 ArkTS 限制规避

  • MIME 与扩展名:示例通过扩展名推断 MIME,若扩展名缺失则从响应头的 Content-Type 推断。

  • ArkTS 限制:不建议直接 data.header['Content-Type'] 索引;示例使用序列化 + 正则方式提取避免 ArkTS 索引限制。

// 通过序列化响应头并用正则提取 Content-Typeprivate tryGetContentTypeHeader(headerObj: Object | null): string {  if (!headerObj) return '';  try {    const json = JSON.stringify(headerObj);    if (!json) return '';    const match = json.match(/"content-type"\s*:\s*"([^"]+)"/i);    return match && match.length > 1 ? match[1] : '';  } catch (_) {    return '';  }}
复制代码

保存文件:

const filePath = `${this.filesDir}/${fileName}`;if (fileIo.accessSync(filePath)) {  fileIo.unlinkSync(filePath);}const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);const bytesWritten = fileIo.writeSync(file.fd, fileBuffer);fileIo.closeSync(file);
复制代码

权限:

  • 客户端需要在 entry/src/main/module.json5 声明 ohos.permission.INTERNET 才能进行网络请求。


5. 后端:简单的静态文件下载接口

示例后端路径:d:\code\atoStudy\server,目录 public/ 放置 4 个演示文件。

核心路由:GET /file/:filename

后端的简单目录结构:

image-20251112092243514

// index.js(简版示例)const express = require('express');const path = require('path');const app = express();
app.get('/file/:filename', (req, res) => {  const filename = req.params.filename;  const filePath = path.join(__dirname, 'public', filename);  res.sendFile(filePath); // 或根据需要设置 Content-Type});
app.listen(3000, () => {  console.log('Server listening on http://localhost:3000');});
复制代码

客户端请求地址示例:

private serverBase: string = "http://192.168.5.2:3000/file";// 组合完整 URL 示例:`${this.serverBase}/1.pdf`
复制代码

注意:请按真实局域网 IP 替换 192.168.5.2,并保证手机/模拟器与后端在同一网络。


6. 快速运行与验证

后端:

  • 安装依赖并启动:npm install && node index.js

  • 确认 public/ 下存在 1.pdf1.png2.png3.png

客户端:

  • 在 module.json5 中确保已声明 ohos.permission.INTERNET

  • 构建并安装到设备/模拟器

  • 点击“下载”,观察逐项状态变化

  • 下载成功后点击“预览”,验证多窗口预览是否正常

图片占位:请补充上述过程的关键截图(如“权限声明处”、“下载成功状态”、“多窗口预览”)。


7. 常见问题与排查

  • 权限错误(如 code=201 / “Permission denied”):检查 ohos.permission.INTERNET 是否声明;确认真机/模拟器的网络可达性。

  • 404 或下载失败:确认后端路由 /file/:filename 存在且文件确实在 public/ 目录内;检查客户端 serverBase 地址是否正确。

  • MIME 与扩展名错配:优先使用后端返回的 Content-Type;如果缺失,则按扩展名推断。

  • UI 不刷新:在 ArkUI 中对数组进行“重新赋值”来触发刷新,避免原地修改元素(例如使用 this.itemStatuses = [...nextStatuses])。


8. 小结

filePreview.openPreview 是 HarmonyOS 文件预览能力的核心,支持一次性打开多文件预览。结合简单的后端静态文件服务与并发下载、响应式状态刷新,能够快速搭建一个“下载即预览”的演示工程。本文的示例工程完整覆盖了从后端文件提供、客户端下载与保存、到预览窗口打开的关键路径,适合作为入门教程与二次扩展的基础。

发布于: 1 小时前阅读数: 2
用户头像

万少

关注

还未添加个人签名 2021-12-02 加入

还未添加个人简介

评论

发布
暂无评论
HarmonyOS preview 预览文件 Kit 的入门讲解_HarmonyOS_万少_InfoQ写作社区