PDF的几种预览方式

707 阅读2分钟

业务需求:微信小程序嵌套 H5 以及浙里办嵌套的 H5 需要预览 PDF。

当时拿到需求时候感觉很简单,但是足足试了三种方法才实现预览,讲一下自己的爬坑之路。

第一种方法:a 标签。 侥幸偷懒方法 利用 a 标签点击跳转新页面预览。

<a href="pdf地址" target="_blank" ></a>

ok,就这,然后兴致匆匆测试了下浏览器测试了下是否能预览,嗯,效果不错,提测提测,然后摸了起来,正在遨游的时候钉钉突然闪动起来,心想莫非测试通过了让我发布线上。

测试(吃鱼大师):你这不行啊,H5 可以,嵌套在微信小程序的 web-view 打不来,显示不支持该方式。

我(BUG 缔造者):肯定是你 PDF 的业务域名没有配置,你在微信小程序体验版打开调试工具看看能行不(心虚)。

赶紧放下二郎腿,问了下度娘,发现我这个天真的方案属实不可以。哎哈我还有第二种偷懒方案,iframe

方案二: iframe。 点击 PDF 弹窗,弹窗里面嵌套 iframe。

<iframe src:"PDF地址" width:"100%" height:"100%" ></iframe>

脸滚键盘,霹雳扒拉。在测试还没答复我之前,把第二种方法发布到了测试环境,接着摸。

安静了一会,又安静了一会,哪个不安静的钉又抖动了,心想是不是通知我发布线上环境。

吃鱼大师:你这不行啊,微信小程序 IOS 可以,部分安卓能打开,但是有部分安卓打不来啊。

狂暴战士:谁,谁的安卓打不开?

吃鱼大师:产品

怕了怕了,产品的打不来,只能看看啥原因了,由于开发时间紧张,这里我没有去细致的探索为啥部分安卓 iframe 无法显示。

回到常用的方案吧,利用三方的 PDF 库来打来文件。

这里我是 Vue 的项目所以我选择的是 vue-pdf。

方案三 vue-pdf/pdf.js

在使用过程中最新的 vue-pdf 会报错,所以问完度娘后大家的建议版本是:
"pdfjs-dist": "2.5.207",//安装这个是看了大家的建议避免出错安装的。
"vue-pdf": "4.2.0",

上代码

<VuePdf :src="pdfSrc" ></VuePdf>

上面这种方式是能够预览 PDF 的,但是只能预览一页,所以需要处理下。我这的需求没有那么多的操作要求,这里就展示下预览所有的简单代码。

<template>
  <div class="visible" v-if="visible">
    <div class="close" @click="handleVisible"></div>
    <div v-if="pdfSrc && pageTotalNum && pageNum" class="handle">
      <div v-if="pageTotalNum > 1" @click="prePage">上一页</div>
      <div>当前页面{{ pageNum }} -- 共{{ pageTotalNum }}页</div>
      <div v-if="pageTotalNum > 1" @click="nextPage">下一页</div>
    </div>
    <div class="iframe" v-if="pdfSrc && !loading">
      <VuePdf :src="pdfSrc" ref="pdf" :page="pageNum"></VuePdf>
    </div>
    <div v-else class="loading">{{ loadingTitle }}</div>
  </div>
</template>
<script>
import VuePdf from "vue-pdf";
// 引入这个的原因是发现部分安卓手机无法预览多页的PDF 所以需要兼容下。
import CMapReaderFactory from "vue-pdf/src/CMapReaderFactory.js";
export default {
  components: { VuePdf },
  props: {
    pdfSrc: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      visible: false,
      pageNum: 1,
      pageTotalNum: 1,
      loading: true,
      loadingTitle: "文件加载中...",
    };
  },
  mounted() {},
  watch: {
    pdfSrc: {
      handler() {
        this.getNumPages();
      },
      immediate: true,
    },
  },
  methods: {
    handleVisible() {
      this.visible = !this.visible;
      //弹窗时候让底部滚动条消失
      // if (this.visible) {
      //   $("body").css("overflow", "hidden");
      // } else $("body").css("overflow", "auto");
    },
    async getNumPages() {
      if (!this.pdfSrc) return;
      this.loading = true;
      this.loadingTitle = "文件加载中...";
      this.pageNum = 1;
      this.pageTotalNum = 1;
      let loadingTask = VuePdf.createLoadingTask({
        url: this.pdfSrc,
        CMapReaderFactory,
      });
      loadingTask.promise
        .then((pdf) => {
          this.pageTotalNum = pdf?.numPages;
          this.loading = false;
        })
        .catch((err) => {
          this.loadingTitle = "文件加载失败!";
          console.error("pdf 加载失败", err);
        });
    },
    prePage() {
      var page = this.pageNum;
      page = page > 1 ? page - 1 : this.pageTotalNum;
      this.pageNum = page;
    },
    nextPage() {
      var page = this.pageNum;
      page = page < this.pageTotalNum ? page + 1 : 1;
      this.pageNum = page;
    },
  },
};
</script>
<style lang="scss" scoped>
.visible {
  box-sizing: border-box;
  z-index: 2;
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  background: #191919;
}
.close {
  box-sizing: border-box;
  width: 100%;
  height: 80px;
  color: #c8c9cc;
  font-size: 24px;
  line-height: 80px;
  text-align: right;
  padding: 12px 24px;
  position: relative;
  &::before {
    content: "x";
    display: inline-block;
    width: 40px;
    height: 40px;
    line-height: 40px;
    text-align: center;
    border-radius: 50%;
    font-size: 40px;
    border: 1px solid #c8c9cc;
    background: #f1f8ff;
  }
}
.handle {
  display: flex;
  align-content: center;
  justify-content: space-around;
  font-size: 24px;
  font-weight: bold;
  color: #f4f4f4;
  height: 80px;
  width: 100%;
  padding: 24px 0;
  box-sizing: border-box;
}
.iframe {
  box-sizing: border-box;
  width: 100%;
  height: calc(100% - 160px);
  margin-bottom: 100px;
}
.loading {
  font-size: 24px;
  color: #f4f4f4;
  height: 80px;
  line-height: 80px;
  text-align: center;
  margin-top: 60px;
}
</style>

遇到的问题:部分安卓手机无法预览多页的 PDF,所有在代码里面做了兼容。

import CMapReaderFactory from "vue-pdf/src/CMapReaderFactory.js";
VuePdf.createLoadingTask({
url: this.pdfSrc,
CMapReaderFactory,
});

总结:美好的一天在于瞎折腾。