uni-app开发app端pdf.js展示文件流
需求背景
有些PDF文件想要可以在app中查看预览,并且可以分享到微信,但是又不想让人直接获得文件存储地址,所以才用了文件流形式返回数据,(属实搞的焦头烂额)话不多说我们直接开始。
引入PDF.js插件
点击上面网址打开后选择右侧下载
下载完成后解压会得到web和build两个文件夹,在项目static目录下新建pdf文件夹并把解压后的两个文件夹放入启动。文件路径如图
代码
首先我们要明白pdf.js并不是能直接展示文件流,也就是arrayBuffer,所以我们需要把
arrayBuffer→blob→blob临时路径才可以让pdf.js正常使用,但是在数据层是无法使用一些webAPI的,所有有一部分操作是在视图层完成,也就是uni-app中的renderjs。
renderjs
1.viewer.mjs
首先打开/static/pdf/web/viewer.mjs文件,搜索defaultOptions.defaultUrl
将其中的value值修改为空(修改默认值防止展示原有缺省pdf)
然后再搜索file origin does not match viewer,找到后将判断注释(允许PDF.js跨域)
2.index.vue
<template>
<view class="dissertation-detail">
<view class="dissertation-content">
<web-view :src="pdfUrl" :webview-styles="webviewStyles"></web-view>
</view>
<view style="display: none">
<input @click="pdfRenderjs.emitData" :propFile="propFile" :change:propFile="pdfRenderjs.getPDF"></input>
</view>
</view>
</template>
<script>
let wv
export default {
data() {
return {
propFile: '',
viewerUrl: '/static/pdf/web/viewer.html?file=',//刚解压的文件地址,用来渲染PDF的html
webviewStyles: {
width: '95%',
position: 'relative',
left: '2.5%'
},
pageForm: {} // 请求接口参数
};
},
onReady() {
// 获取状态栏高度和pdf的高度
// #ifdef APP-PLUS
let currentWebview = this.$scope.$getAppWebview() //此对象相当于html5plus里的plus.webview.currentWebview()。在uni-app里vue页面直接使用plus.webview.currentWebview()无效
setTimeout(function () {
let height = uni.getSystemInfoSync().screenHeight
let top = uni.getSystemInfoSync().statusBarHeight
height = height - top
wv = currentWebview.children()[0]
wv.setStyle({ top, height })
}, 300); //如果是页面初始化调用时,需要延时一下
// #endif
},
async onLoad() {
// 获取pdf文件流详情
await this.getPdfDetailBuffer()
},
methods: {
// 获取pdf内容
async getPdfDetailBuffer() {
const systemInfo = uni.getSystemInfoSync();
if(systemInfo.platform === 'ios'){
this.iosPropFileChange()
}else if(systemInfo.platform === 'android'){
this.androidPropFileChange()
}
},
// ios:pdf文件链接
iosPropFileChange(){
let _this = this
uni.request({
url: `你的请求地址`,
method: 'GET',
success(res) {
_this.propFile = {
pageForm: _this.pageForm,
pdf: res.data
}
},
fail(err) {
console.log(err, '网络错误,或者接口域名错误!')
}
})
},
// android:pdf文件流
androidPropFileChange(){
this.propFile = {
pageForm: this.pageForm
}
},
// 接收视图层数据方法
receiveRenderData(val) {
const systemInfo = uni.getSystemInfoSync();
if(systemInfo.platform === 'ios'){
this.pdfUrl = decodeURIComponent(val)
}else if(systemInfo.platform === 'android'){
this.pdfUrl = this.viewerUrl + val
}
},
},
}
</script>
<script module="pdfRenderjs" lang="renderjs">
export default {
data() {
return {
pdfData: '',
flag: true
}
},
mounted() {},
methods: { //选取的文件上传
getPDF(newValue, oldValue, ownerInstance, instance) {
const systemInfo = uni.getSystemInfoSync();
if(systemInfo.platform === 'ios'){
this.getIosPDF(newValue, oldValue, ownerInstance, instance)
}else if(systemInfo.platform === 'android'){
this.getAndroidPDF(newValue, oldValue, ownerInstance, instance)
}
},
// 发送数据到数据层方法
emitData(e, ownerInstance) {
this.$ownerInstance.callMethod('receiveRenderData', this.pdfUrl)
},
// ios处理方式
getIosPDF(newValue, oldValue, ownerInstance, instance){
if (!newValue || !newValue.pageForm.id) return
if (!this.flag) return false
this.flag = false
// 获取pdf文件流详情
let _this = this
const id = newValue.pageForm.id
const pdf = newValue.pdf
this.pdfUrl = encodeURIComponent(pdf)
this.emitData()
},
// android处理方式
getAndroidPDF(newValue, oldValue, ownerInstance, instance){
if (!newValue || !newValue.pageForm.id) return
if (!this.flag) return false
this.flag = false
// 获取pdf文件流详情
const id = newValue.pageForm.id
let _this = this
const header = {
'Content-Type': 'application/json'
};
fetch('你的请求地址', {
method: 'GET',
headers: header
})
.then(response => response.arrayBuffer()) // 返回结果转为 ArrayBuffer
.then(pdfData => {
try {
// 尝试将响应数据解析为 JSON
const decoder = new TextDecoder('utf-8');
const strData = decoder.decode(pdfData);
const jsonData = JSON.parse(strData);
_this.pageForm.errMsg = jsonData.msg
// 如果成功解析为 JSON,则认为是错误信息
} catch (error) {
// 如果解析失败,则认为是 PDF 文件流并转为blob
let blob = new Blob([pdfData], {
type: 'application/pdf'
});
let pdfUrl = URL.createObjectURL(blob);
_this.pdfUrl = encodeURIComponent(pdfUrl);
console.log(_this.pdfUrl, '_this.pdfUrl');
_this.emitData();
}
})
.catch(err => {
console.log(err, '网络错误,或者接口域名错误!');
});
}
}
}
</script>
踩坑
希望我踩的坑能帮助到你
1.app在数据层不存在一些相应的webAPI,所以需要借助renderjs来处理文件流。
2.在ios无论怎么修改我都无法使用fetch来请求成功,所以我只能在数据层请求后把文件流(blob)传给视图层处理。
3.通过数据层请求好文件流(ArrayBuffer)然后传给视图层,但是在视图层转换blob的时候会出现无地址的问题,这也是为什么返回数据ios(blob)和安卓(ArrayBuffer)两个不同的原因。(其实一个也可以,但是时间着急为了求稳安卓就用了已经走通过的方式展示了)
4.高度和webStyle的设置一定要在onReady内执行,在其他生命周期执行会导致获取的高度有误。
本文作者:异·类
本文链接:https://www.cnblogs.com/yilei-zero/p/18650127
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。