鸿蒙学习实战之路-PDF文档打开与保存最佳实践

最近好多朋友问我:“西兰花啊,我用PDF Kit搞文档操作,咋一开始就卡壳了?打开个PDF文件都报错,这可咋整?” 害,这问题我太熟了!今天我就手把手带你搞定PDF文档的打开和保存,这可是所有PDF操作的第一步,基础打牢了,后面的编辑、批注啥的都不在话下~

一、打开保存PDF,选pdfService还是PdfView?

PDF Kit里有俩工具能处理PDF打开保存:pdfServicePdfView。很多朋友刚上手就犯迷糊,这俩到底有啥区别?啥时候用哪个?别慌,一张表给你整明白:

使用场景 推荐工具
需要编辑PDF(加内容、页眉页脚、水印等) pdfService
只是预览、搜索、批注PDF PdfView

简单来说,如果你要"改"PDF,用pdfService;如果你只是"看"PDF,用PdfView就行啦~

二、核心接口介绍

不管用哪个工具,打开保存PDF的核心接口就俩:

  • loadDocument:加载指定路径的PDF文档
  • saveDocument:保存PDF文档到指定路径

这俩接口在pdfService和PdfView里都有,用法差不多,但参数略有不同,咱们接着往下看~

三、pdfService实现PDF打开与保存

1. 导入必要的模块

import { pdfService } from '@kit.PDFKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { fileIo } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';

2. 初始化与加载PDF

@Entry
@Component
struct PdfPage {
  private pdfDocument: pdfService.PdfDocument = new pdfService.PdfDocument();
  private context = this.getUIContext().getHostContext() as Context;
  private filePath = '';
  @State saveEnable: boolean = false;

  aboutToAppear(): void {
    this.filePath = this.context.filesDir + '/input.pdf';
    this.preparePdfFile();
  }

  // 准备PDF文件(如果不存在则从rawfile复制)
  private preparePdfFile(): void {
    try {
      // 检查文件是否存在
      let res = fileIo.accessSync(this.filePath);
      if (!res) {
        // 从工程资源目录复制PDF文件
        let content: Uint8Array = this.context.resourceManager.getRawFileContentSync('rawfile/input.pdf');
        let fdSand = fileIo.openSync(
          this.filePath, 
          fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE | fileIo.OpenMode.TRUNC
        );
        fileIo.writeSync(fdSand.fd, content.buffer);
        fileIo.closeSync(fdSand.fd);
      }
      // 加载PDF文档
      this.pdfDocument.loadDocument(this.filePath);
    } catch (e) {
      let error: BusinessError = e as BusinessError;
      hilog.error(0x0000, 'PdfPage', `加载PDF失败: ${error.code}, ${error.message}`);
    }
  }
}

3. 实现保存功能

build() {
  Column() {
    // 另存为新的PDF文档
    Button('另存为PDF')
      .onClick(() => {
        try {
          let outPdfPath = this.context.filesDir + '/testSaveAsPdf.pdf';
          let result = this.pdfDocument.saveDocument(outPdfPath);
          this.saveEnable = true;
          hilog.info(0x0000, 'PdfPage', '另存为PDF %{public}s!', result ? '成功' : '失败');
        } catch (e) {
          let error: BusinessError = e as BusinessError;
          hilog.error(0x0000, 'PdfPage', `另存为PDF失败: ${error.code}, ${error.message}`);
        }
      })

    // 保存覆盖原PDF文档
    Button('保存PDF')
      .enabled(this.saveEnable)
      .onClick(() => {
        try {
          // 先备份原文件,避免保存失败导致文件损坏
          let tempDir = this.context.tempDir;
          let tempFilePath = tempDir + `/temp${Math.random()}.pdf`;
          fileIo.copyFileSync(this.filePath, tempFilePath);

          // 创建新的PDF文档实例操作临时文件
          let pdfDocument: pdfService.PdfDocument = new pdfService.PdfDocument();
          let loadResult = pdfDocument.loadDocument(tempFilePath, '');
          
          if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
            // 这里可以添加各种编辑操作,比如加页眉页脚、水印等
            
            // 保存覆盖原文件
            let result = pdfDocument.saveDocument(this.filePath);
            hilog.info(0x0000, 'PdfPage', '保存PDF %{public}s!', result ? '成功' : '失败');
          }
        } catch (e) {
          let error: BusinessError = e as BusinessError;
          hilog.error(0x0000, 'PdfPage', `保存PDF失败: ${error.code}, ${error.message}`);
        }
      })
  }
}

四、PdfView实现PDF打开与保存

如果只是预览PDF,用PdfView更简单:

import { PdfView } from '@ohos/pdf';

@Entry
@Component
struct PdfPreviewPage {
  private pdfController: PdfView.Controller = new PdfView.Controller();

  build() {
    Column() {
      PdfView({
        file: "resources/rawfile/sample.pdf",
        controller: this.pdfController
      })
      .width('100%')
      .height('80%')
      .onLoad(() => {
        console.log("PDF加载成功");
      })
      .onError((error) => {
        console.error("PDF加载失败: " + error.message);
      })

      // PdfView的保存功能需要结合pdfService实现
      Button("保存PDF")
        .onClick(() => {
          // 这里可以调用pdfService的saveDocument方法
          console.log("保存PDF");
        })
    }
  }
}

🥦 西兰花警告

  • 文件路径问题:PDF文件路径一定要用鸿蒙支持的协议(internal://、resources://等),别直接写本地路径!
  • 权限申请:如果要访问应用外的PDF文件,记得申请文件访问权限,不然会直接报错!
  • 异常处理:所有PDF操作都要包在try-catch里,避免文件不存在、格式错误等情况导致应用崩溃!
  • 资源释放:使用pdfService时,操作完文档一定要记得关闭资源,不然会内存泄漏!

🥦 西兰花小贴士

  • 文件备份:保存覆盖原文件前,最好先备份一下,避免保存失败导致原文件损坏
  • 进度监听:loadDocument和saveDocument都支持进度监听,可以用来显示加载/保存进度条
  • 密码处理:如果PDF文件加密了,可以在loadDocument时传入密码参数
  • 大型文件:处理大型PDF文件时,建议使用异步方式,避免阻塞UI线程

五、常见问题与解决方案

1. 加载PDF时提示"文件不存在"

问题:路径写的是"/sdcard/test.pdf",结果报错

解决方案:鸿蒙应用有沙箱限制,不能直接访问外部存储根目录,改用应用沙箱路径:

// 正确写法
let filePath = this.context.filesDir + '/test.pdf';

2. 保存PDF时提示"权限不足"

问题:想保存到用户文档目录,结果提示权限错误

解决方案:申请文件访问权限,在config.json中添加:

"requestPermissions": [
  {
    "name": "ohos.permission.READ_USER_STORAGE",
    "reason": "需要读取用户存储的PDF文件"
  },
  {
    "name": "ohos.permission.WRITE_USER_STORAGE",
    "reason": "需要保存PDF文件到用户存储"
  }
]

3. 打开加密PDF时提示"密码错误"

问题:明明密码是对的,结果还是提示错误

解决方案:检查密码是否包含特殊字符,确保正确转义:

// 正确处理含特殊字符的密码
let password = "my@password123";
let result = pdfDocument.loadDocument(filePath, password);

六、实用资源推荐

📚 推荐资料



我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐