博客 / 詳情

返回

HarmonyOS preview 預覽文件 Kit 的入門講解

本文以實際工程為例,快速上手 HarmonyOS 元服務 的文件預覽能力(PreviewKit),並配套一個後端用於提供示例文件。示例工程路徑:

  • 客户端(HarmonyOS 端):client
  • 後端(Node.js):server

image-20251112090708795

image-20251112090708795


image-20251112091151694

image-20251112091151694

上圖是將 1個pdf文件和3個圖片一起預覽,那麼就只會現實第1個預覽窗口。

下圖是移除pdf文件,將3個同類型的圖片放在一起預覽

image-20251112091518239

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-Type
private 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

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 文件預覽能力的核心,支持一次性打開多文件預覽。結合簡單的後端靜態文件服務與併發下載、響應式狀態刷新,能夠快速搭建一個“下載即預覽”的演示工程。本文的示例工程完整覆蓋了從後端文件提供、客户端下載與保存、到預覽窗口打開的關鍵路徑,適合作為入門教程與二次擴展的基礎。

user avatar smallhuifei 頭像 u_16213634 頭像 maolinlaoshi 頭像 shaogongbra 頭像 taopaodebiandou 頭像 u_16099256 頭像
6 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.