鸿蒙使用pdf.js加载pdf的方案和其定制化

一、背景

由于目前鸿蒙开发生态还没起来,也许在你看到这篇文章时已经有了更好更新的解决方案。

截止到2024.5.24 ,目前在鸿蒙Developer Preview2设备上加载出pdf的方案实在有限。

1. 使用原生web组件直接加载

这种方案不管是在线pdf,还是包内本地pdf资源还是包外沙箱路径pdf都是可以的。

但由于其功能和样式的自定义程度受限,所以这种方案目前的优先级不高。

2. 使用第三方库pdfviewer实现

OpenHarmony-TPC/pdfViewer (gitee.com)

3. 使用pdf.js实现

这种方案是我比较推荐的,因为pdf.js足够强大。

目前的实现可以基于这个项目  hm-pdf-viewer: harmony pdfviewer (gitee.com)

这个项目的实现非常好,但是经过验证,这个基于pdf.js的项目只支持包内本地pdf和在线pdf链接。对于沙箱的pdf是无法加载的。接下来,就解决一下这个问题。 

       

二、如何使用pdf.js加载沙箱路径的pdf文件?

1.  发现问题

经过验证,点击pdf.js工具栏的打开按钮 ,然后通过web组件的onShowFileSelector方法可以监听到h5的input file,从而调起鸿蒙本机的文件管理器用于选择文件。然后这些选择的文件其实就是沙箱路径。然后稍作修改,不需要调起文件管理器,直接把沙箱路径的pdf链接通过handleFileList方法给h5搞过去就行了。然后如图所示就打开了。

 .onShowFileSelector((event) => {
          if (event) {
            event.result.handleFileList([this.url]);
            console.log('url' + this.url)
          }
          return true
        })

那么问题就来了,不可能让用户手动点吧 ?所以需要考虑在页面加载时触发这个input file 事件。

但是这样显然是行不通的,不信你可以自己试试。

由于h5内核限制,不可能通过代码比如 dom 的 click( )方法或者其他鼠标单击事件自动触发文件选择器。 具体可参考

https://github.com/lostvita/blog/issues/32icon-default.png?t=N7T8https://github.com/lostvita/blog/issues/32

难道问题无法解决了吗?

2. 解决

由于无法自动触发input file 事件,那么就需要考虑如何绕开它。

经过分析源码(viewer.js),发现在文件选择完成后其实 是通过选择的File对象构造一个url然后打开就行了。

那么思路就来了,我们只需要把沙箱路径的pdf 构造成h5的File对象就行了。

然后我首先通过runJavaScript方法调用在viewer.html的script标签里自定义的openFIle函数,企图把鸿蒙读取的文件对象直接传过去。

然后发现其实会报错,因为这个fs.File对象不能够被h5的js识别。

最后想到了Base64字符串。

因为字符串是通用类型,可以直接当成参数传给h5。只需要在鸿蒙端把沙箱pdf转成base64,然后传给h5,然后h5 在把base64构造成FIle对象即可打开。

import { util } from '@kit.ArkTS';
import fs from '@ohos.file.fs';

export interface IBase64 {
  base64: string;
  fileName: string;
}

export function sandBoxPdfToBase64(url: string) {
  let file = fs.openSync(url, fs.OpenMode.READ_WRITE); // 打开文件
  let stat = fs.statSync(url); // 获取文件状态
  console.info("stat.size:" + stat.size) // 打印文件的长度
  let buf = new ArrayBuffer(stat.size); // 创建一个ArrayBuffer对象
  let base64 = new util.Base64Helper(); // 实例化Base64Helper
  let num = fs.readSync(file.fd, buf); // 读取文件
  let data = base64.encodeSync(new Uint8Array(buf.slice(0, num))) //  转换成Uint8Array
  console.info(`data长度:${data.length}`)
  console.info(`data:${data}`)
  let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true })
  let retStr = textDecoder.decodeWithStream(data, { stream: false }); // 可以把Uint8Array转码成base64
  let fileName = file.name
  //console.info("file fd: " + file.fd);
  fs.closeSync(file);
  //return new Map().set('base64', retStr).set('fileName', fileName) as Map<string, string>;
  return { base64: retStr, fileName: fileName } as IBase64;
}

然后在viewer.html里把base64转成File对象

 <script type="text/javascript">
   
function base64ToBlob(base64) {
    var bstr = atob(base64),
        n = bstr.length,
        u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {
        type: "application/pdf",
    });
}

function blobToFile(blob, fileName) {
    return new File([blob], fileName);
}

function openFile(code, fileName) {
    //base64转File对象
    let blob = base64ToBlob(code)
    let file = blobToFile(blob, fileName)
    PDFViewerApplication.open({
        url: URL.createObjectURL(file),
        originalUrl: file.name
    });
}
    
    </script>

请注意标签位置要放在引入view.js的标签之后

最终就能在页面加载时自动加载沙箱pdf 啦

三、关于定制化

业务需要定制化的话可以去viewer.css 和 viewer.js里修改。

不需要哪个按钮直接在类名加个hidden即可,不要直接删除,否则会导致错误。

由于代码太多太散,这里不方便展示,所以定制化的话需要自己去研究了,还是相对比较容易的。

这里展示一下效果图仅供参考,起码知道可实现什么效果。

修改前 :

修改后 :

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值