首页 前端知识 vue-pdf使用及注意事项

vue-pdf使用及注意事项

2024-05-07 13:05:41 前端知识 前端哥 494 814 我要收藏

vue-pdf(vue预览pdf文件)的使用和注意事项

  • 需求简介
  • vue-pdf简介
  • 其它pdf预览方式
    • 一、HTML标签
    • 二、插件形式
  • vue-pdf的安装(有坑)
  • vue-pdf的使用
    • 一、安装file-loader(坑)
    • 二、本地测试pdf预览,两种方式(坑)
      • 1. pdf文件在public文件夹下
      • 2. 使用java后台返回文件流
    • 二、线上测试

需求简介

   近期公司有一个需求需要在移动端实现pdf文件预览,因为移动端是用vue开发的,所以最终选择使用vue-pdf插件进行开发。

vue-pdf简介

   vue-pdf是一个pdf预览的插件,主要在vue中使用,开发者是国外的(vue生态系统很大,也有国外的大佬参与了一些插件开发,主要是一些npm包),使用起来较为简单,就是坑有点多,如果公司要求没有那么严格,只是简单的预览功能,对样式也没有过多要求的话这个插件很不错。

官网链接:vue-pdf,https://hub.fgit.cf/FranckFreiburger/vue-pdf?tab=readme-ov-file
如果打不开,可以使用下面这个网址将链接复制进去加速访问
https://github.ur1.fun/

   尽量面向官网学习,这里有最为权威和全面的教程,就是英文页面有点烦人,适应一下,如果遇到问题可以百度看看解决方案。

简单总结一下官网列出的一些方法和属性

  • :src,用于绑定文件的URL
  • :page,用于展示第几页pdf,如果一个页面只渲染一页这个就会用到,翻页就是改变这个绑定的值
  • :rotate,旋转的角度,不常用
  • @password,当文档有密码时使用,不常用
  • @process,文档加载的进度,不常用
  • @loaded,当文档第一次加载时触发,不常用
  • @page-loaded,当页面加载时使用,当pdf翻页时会使用
  • @num-pages,pdf文件的总页数
  • @error,文件加载错误时触发
  • @lin-clicked,点击内部链接时触发,不常用
  • createLoadingTask,最常用的方法,加载pdf时使用
  • print,打印pdf的方法

其它pdf预览方式

一、HTML标签

  • iframe 标签
  • embed 标签
  • object 标签

   这三种都是使用HTML原生的标签,使用起来要么就是兼容性很差,要么就是不好理解或者不好开发,一般不建议使用。

二、插件形式

  • pdf.js
  • PDFObject

   大多数我们使用的就是插件,开发速度较快,其中最有名的就是pdf.js,现在很多pdf预览的第三方插件都是基于pdf.js封装的(包括vue-pdf),有兴趣的伙伴可以研究一下。

vue-pdf的安装(有坑)

安装命令:
npm install --save vue-pdf

   这是最基本的安装命令,但是这样安装极有可能会在最开始引入的时候import pdf form "vue-pdf就报错,如下:

MainTemplate.hooks.hotBootstrap has been removed (use your own RuntimeModule instead

   然后就是一顿找为什么会报这个错,网上大多数都是说版本的问题,需要降低版本,因为我们默认安装的话是最新版本(现在是4.3.0),这个版本兼容性不是很好,所以出现这个错误我们首先需要做的是:

  1. 使用命令卸载之前安装的vue-pdf

npm uninstall vue-pdf

  1. 指定版本安装(4.2.0)

npm install --save vue-pdf@4.2.0
npm install pdfjs-dist@2.5.207

   这里一定要卸载之前的版本,有时候就会有冲突,觉得不妥的伙伴可以在卸载后删除node_modules文件夹,重新加载一下再安装。
   一般情况下,执行上述命令就可以解决这个问题,但有时候在执行完后还是不行,这时候如果你执意要搜索为什么,答案普遍都是这个,有可能还有有一个说是脚手架版本太高,也就是@vue/cli版本是4.5以上,需要降一下版本,这时候可不要贸然去降低脚手架版本了,原因可能不是这个,注意了,vue-pdf的最新版本是2020年发布的,距离现在已经有3年了,我们都忽视了node版本,那时候的node版本可没有我们现在安装的那么高,所以就会出现不兼容的情况,(当时我使用的是node16,切换到14版本就可以了)这时候换个node版本,删除node_modules文件夹重新编译就可以解决引入报错的问题,如果不想卸载node可以参考我之前的nvm教程,快速切换node版本。

nvm使用

vue-pdf的使用

一、安装file-loader(坑)

npm install file-loader

   这里安装file-loader是做后面的本地测试用的,如果不安装则会导致本地测试的时候报错,无法识别pdf文件。Warning: Indexing all PDF objects

webpack配置

  1. 如果没有使用webpack链式编程,那在vue-config.js里面加一个
module.exports = defineConfig({
  ···
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.(png|jpe?g|gif|pdf)$/i,
          loader: 'file-loader',
          options: {
            name: '[path][name].[ext]',
          },
        },
      ],
    }
  }
})
  1. 如果使用了webpack链式编程,也就是chainWebpack,配置如下
    在这里插入图片描述

二、本地测试pdf预览,两种方式(坑)

提前准备好一个pdf文件,实在找不到就新建一个docx文件,使用office导出为pdf文件。
在这里插入图片描述
   注意了:这里本地测试也分两种,如果是直接将pdf文件放在vue的工作环境下,必须放在public文件夹下面,不然使用require会报错;另外一种是后台返回文件流,之后会说怎么操作。

1. pdf文件在public文件夹下

      <pdf
          v-for="i in pageCount"
          :src="pdfUrl"
          :key="i + 'pdf'"
          :page="i"
          class="pdf-item"
      ></pdf>

vue代码

    /** pdf加载 */
    async previewFile() {
      try {
        let loadingTask = pdf.createLoadingTask(this.pdfUrl);
        loadingTask.promise.then(pdf => {
          this.showPreviewFile = true;
          this.pageCount = pdf.numPages;
        }).catch((e) => {
          console.log("pdf初始化错误", e);
          Notify("文件初始化失败,请返回下载该文件查看")
        })
      } catch (e) {
        this.$router.go(-1)
        console.log("pdf加载出错了", e);
      }
    },

js代码

pdfUrl是我的父组件传过来的(之后会附上完整代码),父组件中我的url是这样处理的

var testPdfUrl = require('@process/public/A票_8089139370.pdf')

   这样写看起来没有什么毛病,使用require是为了得到pdf的路径,如果你是直接使用在标签上是没有什么问题的,但在实际操作中肯定是要把链接作为变量定义在data中,可是这样写报错了。
ype check failed for prop "pdfUrl"
   这里实际上是有一个坑的,如果你的require是使用在scipt标签中,解析出来的可不是一个string类型的连接哦,是一个对象,在对象中包含链接的属性,在控制台打印出来如下:
在这里插入图片描述
   其实最终file-loader会将pdf文件解析成一个物理路径返回,所以在真实测试的时候,需要将上面的代码稍微改造一下:

var testPdfUrl = require('@process/public/A票_8089139370.pdf').default

   这样就可以实现基本的pdf预览,但一般的公司需求这样是满足不了的,大多数pdf都会有水印和签名的需求,现在的写法是展示不了水印和签名的,所以我们针对水印进行再次改造。

import CMapReaderFactory from 'vue-pdf/src/CMapReaderFactory.js' //引入水印依赖

   在vue中引入水印依赖,之后改造pdf加载函数。

        let loadingTask = pdf.createLoadingTask({
          url: this.pdfUrl,
          CMapReaderFactory
        });
        loadingTask.promise.then(pdf => {
          this.pdfUrl = loadingTask
          this.showPreviewFile = true;
          this.pageCount = pdf.numPages;
        }).catch((e) => {
          console.log("pdf初始化错误", e);
          Notify("文件初始化失败,请返回下载该文件查看")
        })

   在createLoading函数中增加了水印的配置,这样就可以正常加载水印的样式。而签名有点麻烦,经过不断查询资料最终选择了换包,将vue-pdf替换为vue-pdf-signature,这是一位大佬在npm发版的支持签名的包,经过测试没有出现问题,这里我们只需要在package.json中加入"vue-pdf-signature": "^4.2.7",直接npm i即可。最终改造的引入就是

import pdf from "vue-pdf-signature";
import CMapReaderFactory from "vue-pdf-signature/src/CMapReaderFactory"

2. 使用java后台返回文件流

   这种实现方式就是前台发起请求,java后台返回一个文件流,之后前台处理成URL使用,当然一般这种使用的较少,如果你的pdf文件不大可以使用,有时候用来解决跨域问题比较好使,这个之后会说。

后台代码

    @GetMapping("/testWordView")
    @ResponseBody
    public void testWordView(HttpServletResponse response, HttpServletRequest request)  {
        File file = new File("D:/personFile/word/test.docx");
        if (file.exists()) {
            OutputStream os = null;
            try {
                FileUtilService.setDownLoadName(request, response, "测试word文件.docx");
                os = response.getOutputStream();
                os.write(FileUtils.readFileToByteArray(file));
                os.flush();
                os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

前台代码:注意,要使用await关键字

    var res = await testWordView()
    this.pdfUrl = encodeURIComponent(this.getObjectURL(res));
    // 将返回的流数据转换为url
    getObjectURL (file) {
      var binaryData = [];
      binaryData.push(file);
      let url = null;
      if (window.createObjectURL !== undefined) { // basic
        url = window.createObjectURL(new Blob(binaryData, {type: 'application/pdf,utf-8'}));
      } else if (window.webkitURL !== undefined) { // webkit or chrome
        try {
          url = window.webkitURL.createObjectURL(new Blob(binaryData, {type: 'application/pdf,utf-8'}));
        } catch (error) {

        }
      } else if (window.URL !== undefined) { // mozilla(firefox)
        try {
          url = window.URL.createObjectURL(new Blob(binaryData, {type: 'application/pdf,utf-8'}));
        } catch (error) {

        }
      }
      return url;
    }

二、线上测试

   刚才本地测试已经通过了,如果放到测试环境可能会出现一个问题,跨域!一般搜索资料会让我们去修改一下源码,这个方式不可取,放到线上就废了,有人可能说我给线上换包,那如果遇上升级node版本啥的,就不好使了,不能采用哦,而且据我根据网上的教程去找那段代码,在4.2.0版本是没有的。
在这里插入图片描述
   这种就是vue-pdf常见的跨域问题,出现的情况就是一般公司的文件是会单独拎出来一个服务器的,如果直接请求就会出现这种跨域问题,解决方式一般有三种

  1. 服务器直接配置支持跨域(不推荐,很不安全)
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
Access-Control-Expose-Headers: Accept-Ranges, Content-Encoding, Content-Length, Content-Range

使用的话就是ip加端口再加你的文件地址。
2. 使用文件流,就是上面测试使用的那种。
3. nginx代理(推荐使用)

location /files {
  proxy_pass 目标服务器以及端口/;
}

   这是公司常用的手段,一般肯定会有的,直接交给运维去配置,我们直接使用即可!

注意了:千万别再本地用测试环境的URL进行测试,除非你用文件流,因为无论你怎么配置都有跨域问题,我试验了好久,最后放到测试环境其实就OK了,pdf-js底层对这里进行了处理,这时候放到测试环境测试就可以了,不要像我一样在本地死磕啊

以上就是我对vue-pdf使用的一些总结,涵盖了大部分使用当中的坑,这里附上我封装的pdf组件。

<template>
  <div class="process-fileView-pdf-container" ref="pdfView">
    <div v-if="showPreviewFile" ref="pdf">
      <pdf
          v-for="i in pageCount"
          :src="pdfUrl"
          :key="i + 'pdf'"
          :page="i"
          class="pdf-item"
      ></pdf>
    </div>
    <div class="pdf-button-group" ref="fileViewBtnGroup">
      <div class="div-item"><van-button class="btn-item back" type="info" @click="goBack">返回</van-button></div>
      <div class="div-item"><van-button class="btn-item big" type="info" @click="scaleD">放大</van-button></div>
      <div class="div-item"><van-button class="btn-item small" type="info" @click="scales">缩小</van-button></div>
    </div>
  </div>
</template>

<script>
import pdf from "vue-pdf-signature";
import CMapReaderFactory from "vue-pdf-signature/src/CMapReaderFactory"
import {Notify} from "vant";

export default {
  name: "PdfView",
  components: {
    pdf
  },
  data() {
    return {
      // pdf总页数
      pageCount: 1,
      // 缩放
      scale: 100,
      timer: null,
      showPreviewFile: false
    }
  },
  mounted() {
    this.previewFile()
  },
  props: {
    pdfUrl: {
      type: String,
      default: ''
    },
  },
  watch: {
    pdfUrl: {
      handler(newVal, oldVal) {
        if (newVal) {
          this.previewFile()
        }
      },
      immediate: false
    }
  },
  methods: {
    /** pdf加载 */
    async previewFile() {
      try {
        let loadingTask = pdf.createLoadingTask({
          url: this.pdfUrl,
          CMapReaderFactory
        });
        loadingTask.promise.then(pdf => {
          this.pdfUrl = loadingTask
          this.showPreviewFile = true;
          this.pageCount = pdf.numPages;
        }).catch((e) => {
          console.log("pdf初始化错误", e);
          Notify("文件初始化失败,请返回下载该文件查看")
        })
      } catch (e) {
        this.$router.go(-1)
        console.log("pdf加载出错了", e);
      }
    },
    /** PDF放大 */
    scaleD() {
      if(this.scale>150) return false
      this.scale += 10;
      this.$refs.pdf.style.width = parseInt(this.scale) + "%";
    },
    /** PDF缩小 */
    scales() {
      if(this.scale<40) return false
      this.scale -= 10;
      this.$refs.pdf.style.width = parseInt(this.scale) + "%";
    },
    /** 返回 */
    goBack() {
      this.$router.go(-1)
    },
  }
}
</script>

<style lang="scss" scoped>
.process-fileView-pdf-container {
  width: 100%;
  height: 100%;
  .pdf-item{
    height: 100vh;
    display: block !important;
  }
  .pdf-button-group {
    position: fixed;
    top: 10px;
    right: 20px;
    .btn-item {
      width: 50px;
      height: 50px;
      border-radius: 100%;
    }
    .div-item {
      margin-bottom: 8px;
    }
    ::v-deep .van-button__text{
      line-height: 20px;
    }
  }
}
</style>
转载请注明出处或者链接地址:https://www.qianduange.cn//article/7368.html
标签
arcgis
评论
发布的文章

JQuery中的load()、$

2024-05-10 08:05:15

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!