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

2024-03-01 1202阅读

温馨提示:这篇文章已超过386天没有更新,请注意相关的内容是否还可用!

需求

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

vue 移动端app预览和保存pdf踩坑
(图片来源网络,侵删)

前置条件

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

pdf5实现

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

  
保存
// 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, //手势缩放最大倍数 默认3 scrollEnable: 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 { } }, }, };

pdf.js 实现

使用pdf.js实现,需要下载文件包,具体实现参考

vue开发h5页面如何使用pdf.js实现预览pdf

  
保存
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("体检通知单"); }, }, };

刚开始呢,也是遇到了跨域,我想着看看前端能不能处理,先把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;    }    ```
      
    • 修改PDF.js自身行为:

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

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

  
保存
// 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, //手势缩放最大倍数 默认3 scrollEnable: 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("体检通知单"); }, }, }; .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; }

但是运行起来会有问题,

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端的实现方法呢,就是

 //打开系统浏览器
        @JavascriptInterface
        public void openSystemBrowser(String param) {
           Gson gson = new Gson();
           Map 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();
          }
        }
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]