HarmonyOS 7 新特性(四十八)|PDF Kit:预览、批注与原子保存
PDF 预览看似是“把文件交给组件”,真正上线后却会遇到大文件首开慢、加密文档、沙箱路径、分页布局、搜索高亮、批注保存、外链跳转、内存释放和编辑冲突。PDF Kit 提供 pdfService、pdfViewManager.PdfController 和 PdfView 等能力;API 26 适配的重点,是把控制器生命周期和业务文档状态设计完整。
本文以合同审阅场景为例,搭建一条“安全导入—加载—预览—搜索—批注—原子保存—释放”的工程链路,并明确哪些接口需要按设备与 SDK 再验证。

一、先区分文档与视图状态
文件本身的版本、校验值和权限,不应混在页面缩放、当前页与布局状态中。
interface PdfDocumentState {
documentId: string
localPath: string
sha256: string
version: number
readOnly: boolean
encrypted: boolean
}
interface PdfViewState {
pageIndex: number
zoom: number
continuous: boolean
layout: 'SINGLE' | 'DOUBLE'
searchQuery?: string
}
视图可随时重建,文档状态则需要持久化和并发控制。
二、导入文件先进入应用沙箱
无论文件来自资源、选择器、分享还是下载,都先复制到受控目录,限制大小、扩展名和来源。
interface PdfImportPolicy {
maxBytes: number
allowedMime: string[]
requireHash: boolean
}
function validateImport(meta: { size: number; mime: string }, p: PdfImportPolicy) {
if (meta.size > p.maxBytes) throw new Error('PDF_TOO_LARGE')
if (!p.allowedMime.includes(meta.mime)) throw new Error('UNSUPPORTED_TYPE')
}
不要直接长期依赖临时 URI。复制时使用随机文件名,原始文件名只作为显示信息并做字符清洗。
三、控制器与页面生命周期成对
官方预览示例使用 new pdfViewManager.PdfController()、loadDocument() 和 PdfView。页面销毁或切换文档时必须释放。
import { pdfService, pdfViewManager, PdfView } from '@kit.PDFKit'
@Entry
@Component
struct PdfReaderPage {
private controller: pdfViewManager.PdfController =
new pdfViewManager.PdfController()
async aboutToAppear() {
const result = await this.controller.loadDocument(this.localPath)
if (result !== pdfService.ParseResult.PARSE_SUCCESS) {
this.showLoadError(result)
}
}
aboutToDisappear() {
this.controller.releaseDocument()
}
}
生命周期方法和 releaseDocument 的异常处理以目标 SDK 为准;核心原则是一个已加载文档只由明确所有者释放一次。
四、加载过程做状态化错误处理
type PdfLoadState =
| { kind: 'COPYING'; progress: number }
| { kind: 'PARSING' }
| { kind: 'READY'; pageCount: number }
| { kind: 'PASSWORD_REQUIRED' }
| { kind: 'FAILED'; code: string; retryable: boolean }
损坏、加密、权限不足、空间不足和内存不足的恢复方式不同。不要统一显示“打开失败”。加密文档密码不得写入日志或持久化明文。

五、预览参数按设备形态自适应
手机默认单页连续滚动,平板横屏和 PC 可使用双页;缩放与页面适配需要保留用户上下文。
interface PdfLayoutPolicy {
widthVp: number
orientation: 'PORTRAIT' | 'LANDSCAPE'
input: 'TOUCH' | 'MOUSE'
}
function chooseLayout(p: PdfLayoutPolicy): 'SINGLE' | 'DOUBLE' {
return p.widthVp >= 840 && p.orientation === 'LANDSCAPE'
? 'DOUBLE' : 'SINGLE'
}
通过控制器提供的页面布局、连续滚动、适配、间距和缩放接口设置视图,切换布局后尽量保持当前页和阅读锚点。
六、搜索采用可取消会话
PdfController 提供关键字搜索、清除搜索和索引控制能力。用户连续输入时取消或覆盖旧会话。
interface SearchSession {
id: number
query: string
startedAt: number
}
class PdfSearchCoordinator {
private current = 0
next(query: string): SearchSession {
return { id: ++this.current, query: query.trim(), startedAt: Date.now() }
}
isCurrent(id: number) { return id === this.current }
}
空查询立即清除高亮。对超大文档显示搜索进度,结果跳转后保留“第 N/总数”反馈。
七、高亮与文本选择遵守坐标语义
PDF 坐标、屏幕坐标、缩放和旋转可能不同。业务侧保存页码与文档坐标,不保存屏幕像素。
interface DocumentRect {
pageIndex: number
left: number
top: number
right: number
bottom: number
rotation: number
}
旋转或单双页切换后重新投影显示。选择回调、矩形变化和文本选择监听的注册时机按 API 文档处理。
八、批注用命令日志保证可撤销
高亮、下划线、文本注释和形状等编辑不要直接散落调用。统一转成命令,记录操作者、文档版本和撤销信息。
interface AnnotationCommand {
commandId: string
documentVersion: number
pageIndex: number
type: 'ADD' | 'UPDATE' | 'DELETE'
annotationId?: string
payload: Record<string, unknown>
createdAt: number
}
控制器的 enableAnnotation、添加/更新/删除批注等接口只由命令执行器调用,便于审计和失败重试。
九、保存使用临时文件加原子替换
直接覆盖原文件,崩溃或磁盘不足会损坏唯一副本。
async function atomicSave(target: string) {
const temp = `${target}.saving`
await pdfGateway.saveDocument(temp)
await fsyncFile(temp)
await verifyPdf(temp)
await replaceFile(temp, target)
}
保存前比较文档版本,发现服务端或其他端已更新时进入冲突流程,不静默覆盖。
十、外链点击必须验证
PDF 内可包含 URI 跳转。registerActionClickListener 收到目标后,先校验协议、域名和用户意图。
function allowedPdfLink(raw: string): boolean {
const url = new URL(raw)
if (url.protocol !== 'https:') return false
return ['docs.example.com', 'help.example.com'].includes(url.hostname)
}
未知域名用系统确认页或阻止,不允许 javascript:、file: 和任意自定义 scheme 静默执行。
十一、大文件的内存与缩略图治理
不要一次将每页转换成全分辨率 PixelMap。缩略图按可见窗口生成,设 LRU 上限,页面离开后释放。
interface ThumbnailBudget {
maxEntries: number
maxBytes: number
prefetchBefore: number
prefetchAfter: number
}
const budget: ThumbnailBudget = {
maxEntries: 24,
maxBytes: 48 * 1024 * 1024,
prefetchBefore: 2,
prefetchAfter: 4
}
滚动速度快时减少后台生成,避免与正文渲染争抢资源。
十二、隐私与生命周期清理
合同、病历和账单类 PDF 属于敏感内容。进入后台时按业务要求遮罩最近任务缩略图;登出清除临时文件、搜索词和批注草稿。
interface PdfRetentionPolicy {
cacheTtlMs: number
removeOnLogout: boolean
redactRecentTask: boolean
allowExport: boolean
}
导出、分享和保存副本必须检查权限与水印策略。
十三、测试矩阵
const pdfCases = [
'1-page-small', '1000-page-large', 'encrypted', 'corrupted',
'mixed-page-size', 'rotated-page', 'annotation-save',
'disk-full', 'external-link', 'background-restore'
]
手机、平板和 PC/2in1 分别验证触摸、鼠标、键盘、窗口缩放与单双页。性能报告记录首屏、翻页、搜索、保存和峰值内存。

十四、上线检查清单
- 外部 PDF 复制到受控沙箱并校验大小、类型和哈希;
- 控制器加载与释放成对;
- 加密、损坏、空间不足等错误可区分;
- 布局按设备形态切换并保留阅读位置;
- 搜索会话可取消,空查询清理高亮;
- 批注操作可撤销、可审计;
- 保存采用临时文件与原子替换;
- PDF 外链经过协议和域名校验;
- 缩略图和 PixelMap 有内存预算;
- 登出、后台和导出遵守隐私策略。
结语
PDF Kit 提供了丰富的预览与编辑原子能力,但产品质量取决于控制器生命周期、文件一致性、安全跳转和资源预算。把文档状态与视图状态分离,用可取消搜索、命令化批注和原子保存构建闭环,再覆盖大文件、加密与多设备输入,PDF 功能才能从“能打开”走向可用于真实业务。
官方参考
- PDF Kit 产品介绍:https://developer.huawei.com/consumer/cn/sdk/pdf-kit/
- 预览 PDF 文档:https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/pdf-pdfview-component
- PDF Kit API 总览:https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/api/pdf-api
更多推荐

所有评论(0)