欢迎光临
我们一直在努力

HarmonyOS ArkWeb 系列之网页秒变PDF:createPdf 完整指南

文章目录

      • createPdf 是什么
      • 配置参数说清楚
      • Callback 方式
      • Promise 方式
      • 完整流程图
      • 那个最容易忽略的坑
      • 权限配置
      • 写在最后

能把一张网页直接转成 PDF,保存到本地——这个需求在报表、电子凭证、文档生成场景里非常常见。HarmonyOS 的 Web 组件内置了 createPdf 接口,不需要引入任何第三方库,直接调用就行。 今天把 Callback 和 Promise 两种写法都讲清楚,并且重点说清楚那个最容易忽略的坑。

createPdf 是什么

WebviewController.createPdf() 是 Web 组件控制器上的一个方法,它把当前 Web 组件渲染的页面内容截取成 PDF 格式,以二进制数据流的形式返回给你。

你拿到这个数据流之后,可以用 fileIo 把它写成 .pdf 文件,存到应用沙箱目录里,或者分享给用户下载。

配置参数说清楚

调用 createPdf 前,需要传一个 PdfConfiguration 对象:

// PdfConfiguration 的完整字段(单位:英寸)
const pdfConfig: webview.PdfConfiguration = {
width: 8.27, // 页面宽度,8.27 英寸 ≈ A4 纸宽度
height: 11.69, // 页面高度,11.69 英寸 ≈ A4 纸高度
marginTop: 0, // 上边距
marginBottom: 0, // 下边距
marginRight: 0, // 右边距
marginLeft: 0, // 左边距
shouldPrintBackground: true // 是否打印背景色/背景图
};

注意单位是英寸,不是毫米也不是像素。A4 纸尺寸换算:

  • 宽:210mm ÷ 25.4 ≈ 8.27 英寸
  • 高:297mm ÷ 25.4 ≈ 11.69 英寸

shouldPrintBackground: true 建议开启,否则有背景色的页面导出来是白底,效果很差。

Callback 方式

import { fileIo } from '@kit.CoreFileKit';
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';

@Entry
@Component
struct Index {
controller: webview.WebviewController = new webview.WebviewController();
pdfConfig: webview.PdfConfiguration = {
width: 8.27,
height: 11.69,
marginTop: 0,
marginBottom: 0,
marginRight: 0,
marginLeft: 0,
shouldPrintBackground: true
};

build() {
Column() {
Button('保存为 PDF(Callback 方式)')
.onClick(() => {
this.controller.createPdf(
this.pdfConfig,
(error, result: webview.PdfData) => {
if (error) {
console.error(`生成PDF失败: ${(error as BusinessError).message}`);
return;
}
try {
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let filePath = context.filesDir + '/output.pdf';
let file = fileIo.openSync(filePath,
fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);

// result.pdfArrayBuffer() 返回 Uint8Array,.buffer 取得背后的 ArrayBuffer
fileIo.write(file.fd, result.pdfArrayBuffer().buffer)
.then((writeLen: number) => {
console.info(`PDF写入成功,文件大小: ${writeLen} bytes`);
})
.catch((err: BusinessError) => {
console.error(`写入失败: ${err.message}, code: ${err.code}`);
})
.finally(() => {
fileIo.closeSync(file); // 无论成功失败都要关闭文件句柄
});
} catch (resError) {
console.error(`处理PDF数据时出错: ${(resError as BusinessError).message}`);
}
}
);
})

Web({ src: 'https://www.example.com', controller: this.controller })
.width('100%')
.layoutWeight(1)
}
.height('100%')
.width('100%')
}
}

Promise 方式

import { fileIo } from '@kit.CoreFileKit';
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';

@Entry
@Component
struct Index {
controller: webview.WebviewController = new webview.WebviewController();
pdfConfig: webview.PdfConfiguration = {
width: 8.27,
height: 11.69,
marginTop: 0,
marginBottom: 0,
marginRight: 0,
marginLeft: 0,
shouldPrintBackground: true
};

build() {
Column() {
Button('保存为 PDF(Promise 方式)')
.onClick(() => {
this.controller.createPdf(this.pdfConfig)
.then((result: webview.PdfData) => {
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let filePath = context.filesDir + '/output.pdf';
let file = fileIo.openSync(filePath,
fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);

return fileIo.write(file.fd, result.pdfArrayBuffer().buffer)
.then((writeLen: number) => {
console.info(`PDF写入成功,文件大小: ${writeLen} bytes`);
return file;
})
.catch((err: BusinessError) => {
console.error(`写入失败: ${err.message}`);
return file;
})
.then((file) => {
fileIo.closeSync(file);
});
})
.catch((err: BusinessError) => {
console.error(`生成PDF失败: ${err.message}, code: ${err.code}`);
});
})

Web({ src: 'https://www.example.com', controller: this.controller })
.width('100%')
.layoutWeight(1)
}
.height('100%')
.width('100%')
}
}

两种方式功能完全一样,Promise 方式更现代,链式调用更清晰。如果你的项目支持 async/await,可以进一步简化:

async function savePdf(controller: webview.WebviewController, context: common.UIAbilityContext) {
try {
const result = await controller.createPdf({
width: 8.27, height: 11.69,
marginTop: 0, marginBottom: 0, marginRight: 0, marginLeft: 0,
shouldPrintBackground: true
});

const filePath = context.filesDir + '/output.pdf';
const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
try {
const writeLen = await fileIo.write(file.fd, result.pdfArrayBuffer().buffer);
console.info(`PDF生成成功,大小: ${writeLen} bytes,路径: ${filePath}`);
} finally {
fileIo.closeSync(file);
}
} catch (err) {
console.error(`PDF生成失败: ${(err as BusinessError).message}`);
}
}

完整流程图

那个最容易忽略的坑

必须等页面渲染完成再调用 createPdf!

如果页面还在加载中就调用 createPdf,生成的 PDF 可能是空白的,或者只有部分内容。

正确做法是监听 onPageEnd 事件,在页面加载完成后再允许用户触发 PDF 生成:

@Entry
@Component
struct Index {
controller: webview.WebviewController = new webview.WebviewController();
@State pageReady: boolean = false;

build() {
Column() {
Button('保存为 PDF')
.enabled(this.pageReady) // 页面未加载完时按钮禁用
.onClick(() => {
if (!this.pageReady) return;
// 调用 createPdf…
})

Web({ src: 'https://www.example.com', controller: this.controller })
.onPageEnd(() => {
this.pageReady = true; // 页面加载完成后才开放
})
.width('100%')
.layoutWeight(1)
}
}
}

权限配置

生成 PDF 不需要额外权限,但如果 Web 组件加载的是网络地址(https://),需要在 module.json5 里声明网络权限:

{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}

加载本地 $rawfile() 文件则不需要。

写在最后

createPdf 这个接口用法并不复杂,难点在于:

  • 参数单位是英寸,要换算好
  • pdfArrayBuffer() 返回的是 Uint8Array,写文件时要取 .buffer
  • 页面必须加载完成才能调用,否则生成空白 PDF
  • 赞(0)
    未经允许不得转载:171主机测评 » HarmonyOS ArkWeb 系列之网页秒变PDF:createPdf 完整指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址