vue 移动端app预览和保存pdf踩坑

需求

使用Vue开发h5,嵌套到Android和IOS的Webview里,需要实现pdf预览和保存功能,预览pdf的功能,我这边使用了三个库,pdf5,pdf.js,vue.pdf,现在把这三个库在app端的坑分享一下。先说预览的,保存的实现等会再说

前置条件

用第三方库访问pdf,很可能会出现跨域的问题,这个需要后端来处理一下。具体怎么处理,自行百度。我用pdf.js访问的时候,尝试过前端解决跨域,可以参考一下

pdf5实现

先说pdf,这个集成和实现都很简单,但是有个问题,页数多的话,一直现在加载中,并不能加载成功,始终在第一页,这个问题暂时没解决,有大佬知道的话可以指点一下

<template><div style="height: 100vh"><div id="pdf-content" style="height: 60vh"></div><div class="div-task-button"><div class="tasks-button" @click="downloadPdf">保存</div></div></div></div>
</template>// import Pdfh5 from "pdfh5";
// import "pdfh5/css/pdfh5.css";
import pdf from "vue-pdf";
export default {name: "Pdfh5",data() {return {pdfh5: null,title: "通知单",pdfUrl: "", // 如果引入本地pdf文件,需要将pdf放在public文件夹下,引用时使用绝对路径(/:表示public文件夹)};},mounted() {try {let orderItem = JSON.parse(this.$route.query.item);this.title = orderItem.title;this.pdfUrl = orderItem.pdfUrl ;} catch (e) {console.log(e)}this.initPdf();},methods: {initPdf() {this.pdfh5 = new Pdfh5("#pdf-content", {pdfurl: this.pdfUrl, // pdf 地址,请求的地址需要为线上的地址,测试的本地的地址是不可以的lazy: true, // 是否懒加载withCredentials: true,renderType: "svg",maxZoom: 3, //手势缩放最大倍数 默认3scrollEnable: true, //是否允许pdf滚动zoomEnable: true, //是否允许pdf手势缩放});},downloadPdf() {console.log("开始下载");let body = {url: this.pdfUrl,};if (config.isAndroid && window.hesAndroidNative) {window.hesAndroidNative.openSystemBrowser(JSON.stringify(body));} else if (config.isIos && window.webkit) {window.webkit.messageHandlers.openSystemBrowser.postMessage(JSON.stringify(body));} else {}},},
};
</script>

pdf.js 实现

使用pdf.js实现,需要下载文件包,具体实现参考
vue开发h5页面如何使用pdf.js实现预览pdf

<template><div style="height: 100vh"><iframe id="pdfViewer" title="" width="100%" height="60%"></iframe><div class="div-task-button"><div class="tasks-button" @click="downloadPdf">保存</div></div></div></div>
</template>
<script>export default {name: "Pdfh5",data() {return {pdfh5: null,title: "通知单",numPages: undefined,// 可引入网络文件或者本地文件pdfUrl: "", // 如果引入本地pdf文件,需要将pdf放在public文件夹下,引用时使用绝对路径(/:表示public文件夹)};},mounted() {try {let orderItem = JSON.parse(this.$route.query.item);this.title = orderItem.title;this.pdfUrl = orderItem.pdfUrl;} catch (e) {console.log(e)}const pdfLink = '/web/viewer.html?file=' + encodeURIComponent( this.pdfUrl);document.getElementById('pdfViewer').src = pdfLink;// fetch(this.pdfUrl, {//   method: "get",//   mode: "no-cors", //防止跨域//   responseType: "blob",// })//   .then((response) => response.blob())//   .then((blob) => {//     const blobUrl = URL.createObjectURL(blob);//     console.log("blobUrl", blobUrl);//      const pdfLink = '/web/viewer.html?file=' + encodeURIComponent(blobUrl);//      document.getElementById('pdfViewer').src = pdfLink;//   });//this.initPdf();},methods: {},downloadPdf() {console.log("开始下载");let body = {url: this.pdfUrl,};if (config.isAndroid && window.hesAndroidNative) {window.hesAndroidNative.openSystemBrowser(JSON.stringify(body));} else if (config.isIos && window.webkit) {window.webkit.messageHandlers.openSystemBrowser.postMessage(JSON.stringify(body));} else {}// this.pdfh5.download("体检通知单");},},
};
</script>

刚开始呢,也是遇到了跨域,我想着看看前端能不能处理,先把url转成blob,然后在用pdf.js来加载blob,就是注释掉的那几行代码

  fetch(this.pdfUrl, {method: "get",mode: "no-cors", //防止跨域responseType: "blob",}).then((response) => response.blob()).then((blob) => {const blobUrl = URL.createObjectURL(blob);console.log("blobUrl", blobUrl);const pdfLink = '/web/viewer.html?file=' + encodeURIComponent(blobUrl);document.getElementById('pdfViewer').src = pdfLink;});

这么说吧,可以实现,但是又出现了另一个问题,因为使用iframe,Refused to display 'http://192.xxxxxx/' in a frame because it set 'X-Frame-Options' to 'deny'.,这个又需要后台配置
参考文章
VUE使用 iframe 嵌入网页,浏览器报错 x-frame-options deny

这是通义千问上给出的跨域答案,可以作为参考:

PDF.js在加载和渲染远程PDF文件时,如果文件所在的服务器与应用所在服务器不是同一个源(即存在跨域问题),会受到浏览器同源策略的限制,从而导致无法正确加载和显示PDF文件。以下是几种处理PDF.js跨域问题的方法:

  1. 服务器端代理

    • 在后端服务中处理跨域问题,比如在Django等后端框架中,你可以创建一个API接口,这个接口负责从远程服务器获取PDF文件,然后将文件内容作为流返回给前端,绕过浏览器的同源策略。

StreamingHttpResponse import requests

def serve_pdf(request):
# 获取远程PDF文件
response = requests.get(‘http://remote-server.com/path/to/file.pdf’, stream=True)
# 设置响应头,允许跨域等
response_headers = response.headers
response_headers[‘Access-Control-Allow-Origin’] = ‘*’
return StreamingHttpResponse(response.iter_content(chunk_size=1024),
content_type=response.headers[‘Content-Type’],
headers=response_headers) ```

  1. CORS配置

    • 如果你控制远程服务器,可以在服务器上配置CORS(Cross-Origin Resource Sharing)。例如,在Nginx或Apache服务器上,可以通过添加响应头Access-Control-Allow-Origin来允许特定源或者所有源访问资源。
        if ($request_method = 'OPTIONS') {add_header 'Access-Control-Allow-Origin' '*';add_header 'Access-Control-Allow-Methods' 'GET';add_header 'Access-Control-Allow-Headers' 'Range';add_header 'Access-Control-Max-Age' 1728000;add_header 'Content-Type' 'text/plain charset=UTF-8';add_header 'Content-Length' 0;return 204;}if ($request_method = 'GET') {add_header 'Access-Control-Allow-Origin' '*';}try_files $uri $uri/ =404;    }    ```
  2. 修改PDF.js自身行为

    • PDF.js可以通过修改其内部的网络请求模块来发送跨域请求,并且处理相关的预检请求(OPTIONS)以符合CORS规范。
    • 有时可能需要移除或修改代码中对跨域错误处理的部分,但这并不是推荐做法,因为这可能导致安全风险。
  3. Blob URL或FileReader API

    • 可以在客户端JavaScript中利用fetch或其他手段获取远程PDF文件,然后创建一个Blob对象,再将其转换为Blob URL,之后传递给PDF.js viewer。
      .then(response => response.blob())
      .then(blob => {
      const blobUrl = URL.createObjectURL(blob);
      const pdfLink = '/web/viewer.html?file=' + encodeURIComponent(blobUrl);
      document.getElementById('pdfViewer').src = pdfLink;
      });    ```

根据具体场景选择合适的解决方案,优先推荐配置服务器端的CORS策略,其次是通过后端代理转发请求。同时,请确保遵循相关安全原则,避免因放宽跨域策略而引入的安全隐患。

使用vue-pdf实现

最好使用

cnpm install vue-pdf@4.3.0

安装,防止报错,这个实现也简单

<template><div style="height: 100vh"><!-- <div id="pdf-content" style="height: 60vh"></div> --><!-- <iframe id="pdfViewer" title="" width="100%" height="60%"></iframe> --><div class="pdf_wrap"><div class="pdf_list"><pdfv-for="i in numPages":key="i":src="pdfUrl":page="i"style="display: inline-block; width: 100%"></pdf></div><div class="div-task-button"><div class="tasks-button" @click="downloadPdf">保存</div></div></div></div>
</template>
<script>
// import Pdfh5 from "pdfh5";
// import "pdfh5/css/pdfh5.css";
import pdf from "vue-pdf";
export default {name: "Pdfh5",components: {pdf,},data() {return {pdfh5: null,title: "通知单",numPages: undefined,// 可引入网络文件或者本地文件pdfUrl: "", // 如果引入本地pdf文件,需要将pdf放在public文件夹下,引用时使用绝对路径(/:表示public文件夹)};},mounted() {try {let orderItem = JSON.parse(this.$route.query.item);this.title = orderItem.title;this.pdfUrl = pdf.createLoadingTask(orderItem.pdfUrl);console.log(" this.pdfUrl", this.pdfUrl);this.pdfUrl.promise.then((pdf) => {this.numPages = pdf.numPages;})} catch (e) {console.log(e)}// const pdfLink = '/web/viewer.html?file=' + encodeURIComponent( this.pdfUrl);// document.getElementById('pdfViewer').src = pdfLink;// fetch(this.pdfUrl, {//   method: "get",//   mode: "no-cors", //防止跨域//   responseType: "blob",// })//   .then((response) => response.blob())//   .then((blob) => {//     const blobUrl = URL.createObjectURL(blob);//     console.log("blobUrl", blobUrl);//      const pdfLink = '/web/viewer.html?file=' + encodeURIComponent(blobUrl);//      document.getElementById('pdfViewer').src = pdfLink;//   });//this.initPdf();},methods: {initPdf() {this.pdfh5 = new Pdfh5("#pdf-content", {pdfurl: this.pdfUrl, // pdf 地址,请求的地址需要为线上的地址,测试的本地的地址是不可以的lazy: true, // 是否懒加载withCredentials: true,renderType: "svg",maxZoom: 3, //手势缩放最大倍数 默认3scrollEnable: true, //是否允许pdf滚动zoomEnable: true, //是否允许pdf手势缩放});},downloadPdf() {console.log("开始下载");let body = {url: this.pdfUrl,};if (config.isAndroid && window.hesAndroidNative) {window.hesAndroidNative.openSystemBrowser(JSON.stringify(body));} else if (config.isIos && window.webkit) {window.webkit.messageHandlers.openSystemBrowser.postMessage(JSON.stringify(body));} else {}// this.pdfh5.download("体检通知单");},},
};
</script>
<style scoped>
.pdf_wrap {background: #fff;height: 90vh;
}
.pdf_list {height: 65vh;overflow: scroll;
}
.div-task-button {display: flex;align-items: center;width: 100%;justify-content: center;
}
.tasks-button {display: flex;background: white;padding-bottom: 10px;padding-top: 10px;border-radius: 20px;border: 1px solid #4a90e2;justify-content: center;color: #4a90e2;font-size: 16px;margin: 80px 20px;width: 100%;font-weight: 600;
}
</style>

但是运行起来会有问题,

Cannot read properties of undefined (reading ‘catch’)

这个是版本的问题,需要修改源码,要把node_modules\vue-pdf\src\pdfjsWrapper.js中第196行注释掉

//注释掉catch,防止出现Cannot read properties of undefined (reading ‘catch’)
// pdfRender.cancel().catch(function(err) {
// emitEvent(‘error’, err);
// });–>

保存pdf

在pc端很好实现了,但是嵌入到移动端的webview中,包括IOS和android的兼容性之类的问题,不太好实现,最简单的饿一个办法就是Js调用原生app的方法,打开默认浏览器,用浏览器去保存
js方法呢,就是这一段

  downloadPdf() {console.log("开始下载");let body = {url: this.pdfUrl,};if (config.isAndroid && window.hesAndroidNative) {window.hesAndroidNative.openSystemBrowser(JSON.stringify(body));} else if (config.isIos && window.webkit) {window.webkit.messageHandlers.openSystemBrowser.postMessage(JSON.stringify(body));} else {}// this.pdfh5.download("体检通知单");},

android端的实现方法呢,就是

 //打开系统浏览器@JavascriptInterfacepublic void openSystemBrowser(String param) {Gson gson = new Gson();Map<String,String> map = gson.fromJson(param, Map.class);String url = map.get("url");Log.e("url",url);Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));if (intent.resolveActivity(getPackageManager()) != null) {startActivity(intent);} else {// 没有可用的浏览器应用程序Toast.makeText(WebviewBase.this, "没有可用的浏览器应用程序", Toast.LENGTH_SHORT).show();}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/708051.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NFS服务器挂载失败问题

问题 mount.nfs: requested NFS version or transport protocol is not supported背景&#xff1a;现在做嵌入式开发&#xff0c;需要在板端挂载服务器&#xff0c;读取服务器文件。挂载中遇到该问题。 挂载命令长这样 mount -t nfs -o nolock (XXX.IP):/mnt/disk1/zixi01.ch…

c++学习:类

C 中的类&#xff08;class&#xff09;是一种编程结构&#xff0c;用于创建对象。 这些对象可以拥有属性&#xff08;即数据成员&#xff09;和行为 &#xff08;即成员函数或方法&#xff09;。 类的概念是面向对象编程的核心之一&#xff0c;其主要目的是将数据和与数据相…

vue实现水印功能

目录 一、应用场景 二、实现原理 三、详细开发 1.水印的实现方式 2.防止用户通过控制台修改样式去除水印效果&#xff08;可跳过&#xff0c;有弊端&#xff09; 3.水印的使用 &#xff08;1&#xff09;单页面/全局使用 &#xff08;2&#xff09;全局使用个别页面去掉…

绘制窗口及窗口位置变化

为了方便窗口的移动 &#xff0c;及相交窗口关闭之后被遮挡窗口的重绘&#xff0c;因此给每个窗口建立一个内存BUF&#xff0c;等到不涉及内容变更的重绘&#xff0c;只需要将该BUF复制到显存之中。 然而&#xff0c;重绘时存在一个被遮挡时如何操作的问题。比如下图中依次为从…

Rust 从 PyTorch 到 Burn

一、性能轮盘赌 机器码相同&#xff0c;但放置在不同的地址上&#xff0c;性能可能截然不同。 作为软件开发人员&#xff0c;我们经常假设特定代码的性能仅由代码本身和运行它的硬件决定。这种假设让我们在优化代码以获得更好性能时感到有控制力。虽然在大多数情况下这种假设…

【QT+JS】QT和JS 中的正则表达式 、QT跑JS语言

【QTJS】QT和JS 中的正则表达式 、QT跑JS语言 前言正则表达式QT 中的使用QRegExp自带的cap方法怎么用&#xff1f;QRegExp的非贪婪模式与贪婪模式 JS 中的使用 QT 跑JS 语言 前言 在看大佬的系统代码时候&#xff0c;对其中灵活用到的正则表达式和QT 跑JS 语言部分感觉很陌生&…

iOS App冷启动优化:二进制重排

原理 二进制文件中方法的加载顺序&#xff0c; 取决于方法在代码文件中的书写顺序&#xff0c;而不是调用顺序。 应用程序启动时会调用到的方法是有限的&#xff0c;但可能分散在很多个。 由于内存是分页管理的&#xff0c;要加载就要 整页加载。 这就导致很多完全还用不到的方…

C#知识点-18(多线程、同步、异步编程)

多线程 进程&#xff1a;一般指程序中运行的程序&#xff0c;实际作用是为程序再执行过程中创建好所需的环境和资源。 线程&#xff1a;是进程的一个实体&#xff0c;是cpu用来调度执行程序的最小单元&#xff0c;一个进程可以拥有多个线程。 单线程&#xff1a;进程中只有一…

mysql根据某字段分组查询,每组取前10个

mysql中有一个表叫policy&#xff0c;表的字段有id&#xff0c;title&#xff0c;time&#xff0c;spider_name等等&#xff0c;spider_name是爬虫名称&#xff0c;每个爬虫采集的数据都会有这个标识。请问如何根据spider_name爬虫名称&#xff0c;每一个种类获取10条数据&…

网站添加pwa操作和配置manifest.json后,没有效果排查问题

pwa技术官网&#xff1a;https://web.dev/learn/pwa 应用清单manifest.json文件字段说明&#xff1a;https://web.dev/articles/add-manifest?hlzh-cn Web App Manifest&#xff1a;Web App Manifest | MDN 当网站添加了manifest.json文件后&#xff0c;也引入到html中了&a…

FPGA-FIF0模型与应用场景(IP核)

什么是FIFO FIFO (First In First Out) ,也就是先进先出。FPGA或者ASIC中使用到的FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存或者高速异步数据的交互。它与普通存储器的区别是没有外部读写地址线,这样使用起来相对简单,但缺点就是只能顺序写…

python脚本实现全景站点欧拉角转矩阵

效果 脚本 import numpy as np import math import csv import os from settings import *def euler_to_rotation_matrix(roll, pitch, yaw):# 计算旋转矩阵# Z-Y-X转换顺序Rz

随想录算法训练营第四十五天|322.零钱兑换、279.完全平方数

322.零钱兑换 public class Solution {public int CoinChange(int[] coins, int amount) {int[] dpnew int [amount1];int maxint.MaxValue;for(int i0;i<dp.Length;i){dp[i]max;}dp[0]0;for(int i0;i<coins.Length;i){for(int jcoins[i];j<amount;j){if(dp[j-coins[…

leetcode hot100-2

给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。我的解法&#xff0c;是错误解法&#xff0c;只能通过 56 / 126 的测试用例 这个题就是想求&#xff0c;用到的所有字…

java多线程编程(学习笔记)入门

一、多线程创建的三种方式 (1)通过继承Thread本身 (2)通过实现runnable接口 (3)通过 Callable 和 Future 创建线程 其中&#xff0c;前两种不能获取到编程的结果&#xff0c;第三种能获取到结果 二、常见的成员方法 方法名称说明String getName()返回此线程的名称void setNam…

[数据集][目标检测]鸟类检测数据集VOC+YOLO格式11758张200类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;11758 标注数量(xml文件个数)&#xff1a;11758 标注数量(txt文件个数)&#xff1a;11758 标…

Docker之数据卷自定义镜像

文章目录 前言一、数据卷二、自定义镜像 前言 Docker提供了一个持久化存储数据的机制&#xff0c;与容器生命周期分离&#xff0c;从而带来一系列好处&#xff1a; 总的来说Docker 数据卷提供了一种灵活、持久、可共享的存储机制&#xff0c;使得容器化应用在数据管理方面更加…

Git 指令深入浅出【3】—— 远程仓库

Git 指令深入浅出【3】—— 远程仓库 一、远程仓库&#xff08;一&#xff09;基本指令1. 配置 SSH 密钥2. 推送远程仓库其他分支推送远程仓库方法1方法2建立分支链接 方法3 3. 合并分支请求 &#xff08;二&#xff09;.gitignore 忽略文件&#xff08;三&#xff09;标签管理…

MVCC【重点】

参考链接 [1] https://www.bilibili.com/video/BV1YD4y1J7Qq/?spm_id_from333.1007.top_right_bar_window_history.content.click&vd_source0cb0c5881f5c7d76e7580fbd2f551074 [2]https://www.cnblogs.com/jelly12345/p/14889331.html [3]https://xiaolincoding.com/mysql…

基于频率增强的数据增广的视觉语言导航方法(VLN论文阅读)

基于频率增强的数据增广的视觉语言导航方法&#xff08;VLN论文阅读&#xff09; 本文提出的方法很简单&#xff0c;将原始图像增加其他随机图像的高频信息&#xff0c;得到增强的图像作为新的样本&#xff0c;与原始的样本交替训练。背后的动机是&#xff0c;vln模型对高频信息…