最近项目需要开发个新需求,在app里实现pdf在线预览,并且公司为了防止直接访问pdf链接,所以后端考虑用pdf流的格式通过接口返回给前端,一开始以为是个很简单的,毕竟uniapp里成熟的预览pdf插件还是不在少数,以及兼容也很好,感觉1小时就结束了,结果硬是搞了快两天,中间遇到的坑以及试过的方法差点崩溃 -.-,顺便写个博客记录下开发这个需求碰到的一些问题以及最后的解决方案!!
开发思路
1.首先要确定后台返回的pdf流是什么格式;
2.返回的数据是单个还是多个是否需要拼接来生成完整的pdf流;
3.把后端返回的原始的字节数据转换成可以操作的下标数组;
4.把转换过的数据再转换成base64字符串;
5.把base64字符串生成本地路径打开,至此大功告成;
踩到的坑
这里简单描述下自己开发过程中踩到的坑,以及尝试的各种方法,最后才总结出自己的这一套app在线预览pdf的开发思路,如果大佬有更好的解决思路,欢迎在评论区讨论!
1.new Blob( array, options )+ pdf.js 。Blob是前端的一个专门用于支持文件操作的二进制对象 ,将二进制数据存储为一个个体的集合,通常是视频,音乐,或者多媒体文件,目的是为了更好的操作二进制数据对象,使用blob构造函数返回一个新的 Blob 对象。blob 的内容由参数数组中给出的值的串联组成,然后再将blob转成路径URL.createObjectURL(blob),再在项目根目录下新建hybrid文件夹,将下载的pdf.js压缩包解压后,复制到hybrid下的html文件夹中,路径拼接好,再通过web-view打开。这种事最简单的实现方式,但是app里无法使用blob函数,因此这个方法无法使用。具体代码如下:
<template>
<view>
<web-view :src="allUrl"></web-view>
</view>
</template>
<script>
export default {
data() {
return {
viewerUrl: '/hybrid/html/web/viewer.html?file=',
allUrl: '',
}
},
onShow() {
this.getPdf()
},
methods: {
getPdf() {
let params = {
...
}
uni.request({
url: `url`,
method: 'POST',
data: params,
responseType: 'arraybuffer',
success: (response) => {
if (!response) {
uni.showToast({
title: "预览失败",
duration: 2000
});
}
let pdfData = response.data; //pdfData是后端返回的文件流
let blob = new Blob([pdfData], {
type: 'application/pdf'
})
pdfData = URL.createObjectURL(blob) //创建预览路径
this.allUrl = this.viewerUrl+encodeURIComponent(pdfData)
},
fail: err => {
console.log(err)
}
});
}
}
}
</script>
<style>
</style>
2.使用uni.openDocument方法,会去打开手机内可以打开pdf的应用程序来实现预览pdf,不过这个方法跟需求不一样,因此此方法也无法使用。具体代码如下:
// 假设你已经有了PDF流的数据,这里用pdfData代替
const pdfData = '...'; // PDF流数据,通常是一个base64编码的字符串
// 将PDF流数据转换为二进制数组
const binaryArray = [];
for (let i = 0; i < pdfData.length; i++) {
binaryArray.push(pdfData.charCodeAt(i));
}
const uint8array = new Uint8Array(binaryArray);
// 创建一个Blob对象
const blob = new Blob([uint8array], {
type: 'application/pdf' });
// 创建一个URL指向该Blob对象
const url = URL.createObjectURL(blob);
// 使用uniapp.openDocument API打开文档进行预览
uni.openDocument({
filePath: url,
success: function (res) {
console.log('文档打开成功');
},
fail: function (err) {
console.error('文档打开失败', err);
}
});
3.最后才拍案决定的实现效果思路,把base64生成本地路径(storage的路径,尽可放心生成,清除手机缓存即可)再实现app在线预览。具体代码如下:
用到的技术点
1.uni.base64ToArrayBuffer(base64),将 后台返回的Base64 字符串转成 ArrayBuffer 对象;
uni.base64ToArrayBuffe用法
2.new Uint8Array(arrLength),因为ArrayBuffer 为原始的字节序列,不是所谓的“数组”,无法用下标来查看,因此需要使用 Uint8Array来实现访问;
3.uni.arrayBufferToBase64(arrayBuffer),将 ArrayBuffer 对象转成 Base64 字符串;
uni.arrayBufferToBase64用法
4.用到了image-tools.js把base64转换成本地路径 ,再使用plus.io.convertLocalFileSystemURL(path),将本地URL路径转换成平台绝对路径
5.使用uniapp插件市场中的pdf阅读器来实现在app里预览pdf文件
Android iOS PDF阅读器
这里说下,为啥要把base64转成成arrayBuffer,再转换成base64,因为后端返回的是需要合并的pdf流,所以需要进行转换的操作来进行循环合并再生成完整的base64
代码实现
废话不多说,开始实现自己的思路
image-tools.js代码,粘过去放置common或者util文件夹下
function getLocalFilePath(path) {
if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
return path
}
if (path.indexOf('file://') === 0) {
return