前端实现pdf预览-方案合集

4,136 阅读2分钟

背景:在页面中,产品总会希望能放一些pdf格式的文件,例如:协议、发票、说明书等

方法一: 使用HTML标签

本质上依赖浏览器内置的 PDF 预览插件,会有黑色背景和工具条等;

  • iframe标签
<iframe src="/test.pdf" width="100%" height="400px">
    This browser does not support PDFs. Please download the PDF to view it: 
    <a href="/test.pdf">Download PDF</a>
</iframe>
  • embed标签
<embed src="/test.pdf" width="100%" height="400px" />
  • object标签
<object
   data="/test.pdf"
   type="application/pdf"
   width="100%"
   height="400px"
/>

效果图如下:

image.png

注意:(重要)

3个标签都存在浏览器兼容性,并且移动端兼容非常差,使用时需注意用户机型;优点是写法简单,并且支持选中和复制文字;这里更推荐使用iframe,在使用iframe时,iframe属性参考【传送门】,当然还有其他冷门的设置,例如产品不要左侧的菜单和顶部的工具条怎么办?

#page=pagenum, 设置滚动到 pdf 第几页

#zoom=scale,设置缩放比例,缩放值为100表示缩放值为100%

#view=Fit,设置显示区域为适合页面大小

#view=FitH,宽度撑满浏览器窗口,高度自适应

#view=FitV,宽度自适应

#toolbar=1 | 0,打开或关闭工具栏

例如:

<iframe :src=`${pdfUrl}#toolbar=0&view=FitH` width="30%" height="400px">
    This browser does not support PDFs. Please download the PDF to view it:
    <a href="/test.pdf">Download PDF</a>
</iframe>

效果图:

image.png

方法二:使用第三方库

能实现预览pdf的第三方库还是比较多的,但基本上都是基于pdf.js 封装的,使用方法也大同小异,笔者使用过 vue3-pdfpdf-vue3

缺点:包体积较大,部分类型的pdf无法显示

npm i pdf-vue3
<script setup>
import PDF from "pdf-vue3";
</script>

<template>
  <PDF src="/demo.pdf" />
  <!-- <PDF :src="BASE64" /> -->
  <!-- <PDF :src="Uint8Array" /> -->
</template>

效果图:

image.png

shims-vue.d.ts

declare module 'vue3-pdfjs/esm';
<script lang="ts">
import { defineComponent, onMounted, reactive, ref } from 'vue';
import { VuePdf, createLoadingTask } from 'vue3-pdfjs/esm';
import { VuePdfPropsType } from 'vue3-pdfjs/components/vue-pdf/vue-pdf-props'; // Prop type definitions can also be imported
import { PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api';


export default defineComponent({
  name: 'Home',
  components: { VuePdf },
  setup() {
    const pdfSrc = ref<VuePdfPropsType['src']>('https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf')
    const numOfPages = ref(0)

    onMounted(() => {
      const loadingTask = createLoadingTask(pdfSrc.value)
      loadingTask.promise.then((pdf: PDFDocumentProxy) => {
        numOfPages.value = pdf.numPages
      })
    })
    return {
      pdfSrc,
      numOfPages
    }
  }
});
</script>

<template>
  <VuePdf v-for="page in numOfPages" :key="page" :src="pdfSrc" :page="page" />
</template>

效果图:

image.png

注意:(重要)

使用这个方案预览发票等文件时,容易出现文字丢失的情况; image.png

解决方案:

PDFJS.getDocument({
    url: url,
    cMapUrl: "../../static/pdf/cmaps/",
    cMapPacked: true
})

参考: www.cnblogs.com/KingJames/p…

我在上面的 vue3-pdfjs 尝试这样发现不好使,

方法三:转成图片展示

转化后无法复制选中文字,并且不好处理多页的情况,暂时没有深入研究;

参考阅读