uniapp实现在线pdf预览以及下载
在线预览
遇到的问题
后端返回一个url 地址,我需要将在在页面中渲染出来。因为在浏览器栏上我输入url 地址就可以直接预览pdf文件,因此直接的想法是通过web-view
组件直接渲染。有什么问题呢?在h5端能够正常渲染出pdf文件,但是在app端却直接弹出是否下载的弹窗。
如何解决?
利用pdfjs
这个库结合html
页面以及url将pdf的实际地址传给html
页面
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
#btn {
width: 100%;
height: 35px;
background-color: #10aeff;
color: #fff;
line-height: 35px;
text-align: center;
}
</style>
<script src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
<script type="module" src="/static/pdfjs-4.2.67-legacy-dist/build/pdf.js"></script>
<script type="module" src="/static/pdfjs-4.2.67-legacy-dist/build/pdf.worker.js"></script>
<title></title>
</head>
<body>
<canvas id="the-canvas" style="width:100%; height: 80vh"></canvas>
<button id="btn">下载</button>
<script type="module">
var url = '替换为真实地址';
var { pdfjsLib } = globalThis;
pdfjsLib.GlobalWorkerOptions.workerSrc = '/static/pdfjs-4.2.67-legacy-dist/build/pdf.worker.js';
var loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then(function(pdf) {
var pageNumber = 1;
pdf.getPage(pageNumber).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport({scale: scale});
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: context,
viewport: viewport
};
var renderTask = page.render(renderContext);
renderTask.promise.then(function () {
});
});
}, function (reason) {
// PDF loading error
console.error(reason);
});
</script>
</body>
</html>
PS :这个
html
页面是存在服务器的
编写好以上html
界面之后,在uniapp端我们使用web-view
组件来渲染该html
页面
vue
<web-view :src="src" />
使用web-view
所存在的问题
-
web-view
组件的层级高于一切组件,会对其他组件进行覆盖(使用定位也无法解决)-
cover-view
组件会可以解决组件的覆盖问题,但是却无法进行事件处理
vue/* 使用一下无法触发点击事件,但是能够将文字呈现与web-view的上方 */ <view> <web-view :src="src"></web-view> <cover-view> <cover-view @click="handleDownload">下载</cover-view> </cover-view> </view>
-
如何触发下载
通过以上分析,我们知道了在uniapp端已经无法实现下载功能了。那么,真的没有办法了吗?有的,兄弟,有的。通过查看官方文档,我们可以知道web-view
组件提供了一个事件@message
用来接收html
页面发来的消息,那么,我们是不是可以将下载按钮放在html
页面端,通过点击事件通知app端呢?事实证明该方案完全可行。那么页面端怎么向app端发起通信呢?需要结合uni.webview.js
这个库,这个库的作用是什么------他可以让我们在非vue
环境下使用uni
对象。那么我们可以结合这个库使用uni
的postMessage
方法向app发起下载的指令。在app点接收这个指令
js
const btn = document.getElementById('btn');
document.addEventListener('UniAppJSBridgeReady', () => {
btn.addEventListener('click', () => {
uni.postMessage({
data: {
action: 'download'
}
});
})
});
vue
<script>
export default {
methods: {
handleMessage(event) {
const { action } = event.detail.data[0]
if (action && action === 'download') {
this.handleDownload()
}
}
}
}
</script>
实现下载
vue
<script>
export default {
methods: {
handleDownload() {
uni.showLoading({
title: '下载中'
})
plus.downloader
.createDownload(
this.pdfUrl,
{
filename: `file://storage/emulated/0/Download/${new Date().getTime()}.pdf`
},
(d, status) => {
if (status == 200) {
uni.hideLoading()
uni.showToast({
title: '下载成功',
icon: 'none'
})
setTimeout(() => {
const fileSaveUrl = plus.io.convertLocalFileSystemURL(d.filename)
plus.runtime.openFile(fileSaveUrl)
}, 500)
} else {
uni.showToast({
title: '下载失败',
icon: 'error'
})
}
}
)
.start()
}
}
}
}
</script>