0

    面试官:PDF 预览和下载你是怎么实现的?

    2023.05.12 | admin | 132次围观

    前言

    在开发过程中要求对PDF类型的发票提供预览和下载功能,PDF类型文件的来源又包括 H5 移动端和 PC 端,而针对这两个不同端的处理会有些许不同,下文会有所提及。

    针对PDF 预览的文章不在少数,但似乎都没有提及可能遇到的问题,或是提供对应的具体需求场景下如何选择,因此,本文的核心就是结合实际需求场景下,看看目前各种实现方案到底哪一个更适合,当然希望大家可以在评论区对文中的内容进行斧正,或是提供更优质的方案。

    基本要求:

    产品要求:

    PDF 预览

    先抛开上面的各种要求,咱们先总结下目前实现PDF预览的几种常用方式:

    接下来分别看看以上方案如何实现,以及是否符合上述提供的要求!

    / 实现预览标签

    元素将外部内容嵌入文档中的指定位置,此内容由外部应用程序或其他交互式内容源(如浏览器插件)提供。

    说简单点,就是使用来展示的资源是完全交由它所在的环境提供的展示功能,即如果当前的应用环境支持这个资源的展示那么就可以正常展示,如果不支持那就无法展示。

    使用起来也是非常简单:

     type="application/pdf"
     :src="pdfUrl"
     width="800"
     height="600" />

    多数现代浏览器已经弃用并取消了对浏览器插件的支持,现在已经不建议使用标签,但可以使用

    、、、等标签代替。

    标签

    基于的方式和以上差不多,整体效果也一致,这里这就不在额外展示:

     :src="pdfUrl"
     width="800"
     height="600" />

    值得注意的是,即便使用的是但实际展开其内层结构后你会发现:

    其内部还是标签?这是怎么回事,不是说最好不建议使用吗?

    首先来在caniuse[2]查看兼容情况,如下:

    我们再找一个不支持的浏览器,比如IE,来试试效果:

    换成试试,如下:

    显然,在不兼容的环境直接无法显示,而是能够正常识别的,只不过加载的资源无法被IE浏览器处理,即本质原因是IE浏览器根本就不支持对类似PDF等文件的预览,比如当尝试直接在地址栏中输入:3000/src/assets/2.pdf时会得到:

    因此,通常情况下当浏览器不支持内联PDF时,应该提供一个PDF的回退链接,即以下载的方式来实现,而这就是pdfobject[3]做的事情,实际上它的源码内容比较简单,核心就是PDFObject 会检测浏览器对内联/嵌入 PDF 的支持,如果支持嵌入,则嵌入 PDF,如果浏览器不支持嵌入,则不会嵌入 PDF,并提供一个指向 PDF 的回退链接,例如在IE中的表现:

    事实上,这其实只是帮我们少写了一些兼容性的代码而已,也不一定符合大部分人的场景,在这里提到只是因为其与之间存在的联系。

    vue3-pdfjs 实现预览为什么不直接使用pdfjs-dist?

    pdf.js[4]几个明显的可吐槽的点:

    因此,既然已经有基于vue/react封装好的包,这里就直接用来作为演示。

    具体使用

    安装和使用过程可参考vue3-pdfjs[5],具体Vue3示例代码如下:

    "ts">
    import { onMounted, ref } from 'vue'
    import { VuePdf, createLoadingTask } from 'vue3-pdfjs/esm'
    import type { VuePdfPropsType } from 'vue3-pdfjs/components/vue-pdf/vue-pdf-props' // Prop type definitions can also be imported
    import type { PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api'
    import pdfUrl from './assets/You-Dont-Know-JS.pdf'

    const pdfSrc = ref'src']>(pdfUrl)
    const numOfPages = ref(0)

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