纯前端预览

3,318 阅读8分钟

参考

PC端查看pdf文件

a标签跳转

 <a
  href="/libs/static/test.pdf"
  target="_blank"
>年双边合同签订 操作手册</a>

HTML标签(PDF预览)

iframe 标签

HTML 内联框架元素 iframe 表示嵌套的 browsing context。它能够将另一个 HTML 页面嵌入到当前页面中。

(1)根据文件服务器路径预览

<iframe src="./test.pdf" height="900px;" width="800px"></iframe>

(2)将上传的文件blob对象转base64预览

  <iframe
    v-if="file.url.startsWith('data:application/pdf')"
    width="100%"
    height="100%"
    :src="file.url"
    frameborder="0"
  ></iframe>

file.url 为转好的base64

embed 标签

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

embed 标签实现方案代码如下所示:

<embed src="./test.pdf" type="application/pdf" width="100%" height="100%" />

object 标签

HTML object 元素(或者称作 HTML 嵌入对象元素)表示引入一个外部资源,这个资源可能是一张图片,一个嵌入的浏览上下文,亦或是一个插件所使用的资源。

object 标签实现方案代码如下所示:

<object
  data="./test.pdf"
  type="application/pdf"
  width="100%"
  height="100%"
></object>

使用第三方插件(PDF预览)

jquery.media.js

引入依赖

<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/jquery.media.js"></script>  

使用方式1
html代码

html的代码比较简单,只需要插入a标签可以了,其中href为cdn的地址,但url可分为静态写死,和动态生成的。

// 静态写死cdn地址
<a class="media" href="https://apptest.sf-financial.com/wealth/cdn/h5-merchant-wallet-protocol/OpenAccountStatement.pdf"></a>
// 动态生成,这里使用的是angular
<a class="media" ng-href="{{protocalUrl}}"></a>

使用方式2

 <div class="pdf"></div>
 
 $('.pdf-container').media({ width: '400px', height: '500px', autoplay: true, src: './pdf' });

PDF.js

PDF.js 是一款开源的 pdf 文档读取解析插件,可以实现在 html 下直接浏览 pdf 文档。

  • pdf.js 是基于Promise 对象而实现的,不了解的读者可以先去看看MDN 上的解释
  • pdf.js 渲染 pdf 时底层还使用了Web Worker(这会导致我们无法直接在本地运行官网下载的 demo,得在服务器上运行,详情见注意点处),不了解的读者可以去看一下阮一峰老师关于 Web Worker 的文章

PDF.js 主要分为 3 层:
显示层采用核心层,并公开更容易使用的 API 来呈现 pdf 和从文档中获取其他信息。

image.png

方法1

直接使用官方封装好的 viewer.html 来展示自己的 PDF 文档,该方法比较简单,不用去操作 API;而且功能比较齐全,还可复制 pdf 中的文本。

具体步骤如下:

  • 去官网下载打包好的 Prebuilt 版本压缩包,官网:mozilla.github.io/pdf.js/
  • 将需要打开的 PDF 文档放到与 viewer.html 文档的同一目录下
  • 新建一个 html 文件,使用 window.open 方法 或 iframe 标签 来打开 viewer.html,并使用 file 字段来传入 pdf 名字信息

该方法的更多详细信息可参照博文

<!-- 使用iframe -->
<!-- 该方法会受iframe标签兼容性限制 -->
<iframe
  src="./web/viewer.html?file=test.pdf"
  frameborder="0"
  style="height: 800px; width: 100%"
></iframe>
<!-- 使用window.open-->
<!-- 该方法会打开新窗口 -->
<script type="text/javascript">
    window.open("./web/viewer.html?file=test.pdf");//文件和 viewer.html 同路径时
</script>

注意事项:

注意事项一:pdf.js 需要启动服务器才能运行,无法直接打开本地的文档

原因:因为 pdf.js 渲染 pdf 文档时使用了 Web Worker 技术,该 Web Worker 无法读取本地文件。

报错信息:Message: Missing PDF “file:///D:/%E6%A1%8C%E9%9D%A2/pdfjs/web/test.pdf”

解决办法:可通过 live-server 这个插件在本地启动服务器然后打开相应的 html 文件。

具体步骤:

注意事项二:我们的电脑上不能安装 IDM(Internet Download Manager)这类软件或插件。

报错信息 : Unexpected server response (204) while retrieving PDF

原因:因为 IDM 会拦截可下载的资源,会导致页面无法预览。

解决办法:直接卸载或关闭相应的插件、软件;也可以对软件进行相应的设置。更多信息可参照博文

另外使用 pdf.js 打开发票等文件时可能会出现字体显示不全的 bug,解决 pdf.js 无法完全显示 pdf 文件内容的问题

该方法可通过CSP解决pdf预览XSS攻击问题

image.png

方法2

该方法是以图片形式来展示 PDF 文档,所以不能选中文本或复制文本。

PDF.js可以实现在html下直接浏览pdf文档,是一款开源的pdf文档读取解析插件, 非常强大,能将PDF文件渲染成Canvas。PDF.js主要包含两个库文件,一个pdf.js和一个pdf.worker.js, 一个负责API解析,一个负责核心解析。

具体步骤如下:

  • 首先 npm i pdfjs-dist 下载 pdf.js 的 Prebuilt 包
  • 设置 PDFJS.GlobalWorkerOptions.workerSrc 的地址
  • 通过 PDFJS.getDocument(pdf 文件的 url) 处理 pdf 数据,返回一个 PDFDocumentLoadingTask
  • 通过 pdfDoc.getPage(i) 单独获取第 i 页的数据
  • 创建一个 canvas 元素,并设置元素的画布属性
  • 通过 page.render 方法,将数据渲染到画布上、 具体代码如下:
// 第 2 步:设置 workerSrc 地址 (具体包的地址需要依自身项目决定)
import * as PDFJS from "./build/pdf.js";
import pdfjsWorker from "./build/pdf.worker.js";
PDFJS.GlobalWorkerOptions.workerSrc = pdfjsWorker;
const pdfUrl = "./test.pdf"; //具体路径由自身项目决定,另外这可能会涉及跨域问题可参照官网解决
console.log(pdfUrl);
// 第 3 步:使用 PDFJS.getDocument() 处理 pdf 文档
PDFJS.getDocument(pdfUrl).promise.then((pdfDoc) => {
  const totalPages = pdfDoc.numPages; // pdf 的总页数
  const canvasContainer = document.getElementById("#canvasContainer"); //html中需创建一个相应的div容器,用于存放canvas元素
  for (let i = 1; i <= totalPages; i++) {
    // 第4步:使用 pdfDoc.getPage(i) 获取第 i 页的数据
    pdfDoc.getPage(i).then((page) => {
      let scaledViewport = page.getViewport({ scale: 1.5 }); //可通过scale来调节初始的缩放比
      //第5步:创建一个 canvas 元素,并设置元素的画布属性
      let canvas = document.createElement("canvas");
      canvas.setAttribute("id", "the-canvas" + i);
      canvas.height = scaledViewport.height;
      canvas.width = scaledViewport.width;
      let context = canvas.getContext("2d");
      let renderContext = {
        canvasContext: context,
        viewport: scaledViewport,
      };
      //第 6 步: 使用 page.render 将数据渲染到画布上
      page.render(renderContext).promise.then(() => {});
      canvasContainer.appendChildren(canvas); //将canvas元素加入到容器中
    });
  }
});

PDFObject

  • 官网:pdfobject.com/
  • github地址:github.com/pipwerks/PD…
  • 支持:PDFObject 2.0不向后兼容1.0版本,针对现代浏览器设计,支持Chrome, Firefox, Safari (OS X and iOS), IE 9-11, and MS Edge

相对 pdf.js 来说,PDFObject 的使用非常简单。但在手机 webview 使用兼容性不太好。 PDFObject 2.0 不向后兼容 1.0 版本,针对现代浏览器设计,支持 Chrome, Firefox, Safari (OS X and iOS), IE 9-11, and MS Edge。 更多信息可参照官网地址,和PDF 预览之 PDFObject.js 总结

使用步骤:

  • 创建嵌入 PDF 的容器
  • 告诉 PDFObject,插入的 PDF 文件路径,以及插入到哪个容器
  • 可以选择使用 CSS 来指定视觉样式,包括维度、边框、边距等
<!-- 第1步:创建嵌入PDF的容器 -->
<div id="pdf"></div>
<!-- 第2步:告诉PDFObject,插入的PDF文件路径,以及插入到哪个容器 -->
<script src="library/pdfobject.js"></script>
<script>
  PDFObject.embed("uploads/pdfs/dongxuemin.pdf", "#pdf");
</script>
<!-- 第3步:可以选择使用CSS来指定视觉样式,包括维度、边框、边距等 -->
<style>
  .pdfobject-container {
    height: 500px;
  }
  .pdfobject {
    border: 1px solid #ccc;
  }
</style>
<div class="container"></div>

import pdfobject from "@/common/_verdors/pdfobject";
pdfobject.embed(viewLink, '.container');

另外还有许多第三方库可实现 pdf 预览,如:vue-pdf等等。

使用第三方插件(world预览)

docx-preview

  • 安装依赖
npm i docx-preview -S
  • 使用demo
<template>
    <div class="docx-preview-wrap">
        <h1>kaimo test docx-preview</h1>
        <h4>
            <input type="file" id="file" accept=".docx"/>
            <button @click="handlePreview">预览</button>
        </h4>
        <div id="bodyContainer"></div>
    </div>
</template>

<script>
import { defaultOptions, renderAsync } from "docx-preview";
console.log(defaultOptions);
export default {
    name: 'DocxPreview',
    data () {
        return {
            docxOptions: {
                className: "kaimo-docx-666", // string:默认和文档样式类的类名/前缀
                inWrapper:  true, // boolean:启用围绕文档内容的包装器渲染
                ignoreWidth: false, // boolean:禁用页面的渲染宽度
                ignoreHeight: false, // boolean:禁止渲染页面高度
                ignoreFonts: false, // boolean:禁用字体渲染
                breakPages: true, // boolean:在分页符上启用分页
                ignoreLastRenderedPageBreak: true, // boolean:在 lastRenderedPageBreak 元素上禁用分页
                experimental: false, // boolean:启用实验功能(制表符停止计算)
                trimXmlDeclaration: true, // boolean:如果为true,解析前会从​​ xml 文档中移除 xml 声明
                useBase64URL: false, // boolean:如果为true,图片、字体等会转为base 64 URL,否则使用URL.createObjectURL
                useMathMLPolyfill: false, // boolean:包括用于 chrome、edge 等的 MathML polyfill。
                showChanges: false, // boolean:启用文档更改的实验性渲染(插入/删除)
                debug: false, // boolean:启用额外的日志记录
            }
        };
    },
    methods: {
        handlePreview() {
            let file = document.getElementById("file").files[0];
            console.log(file);
            // 将file转为buffer
            let fr = new FileReader();
            fr.readAsArrayBuffer(file);
            fr.addEventListener("loadend",(e) => {
                console.log("loadend---->", e)
                let buffer = e.target.result;
                this.docxRender(buffer);
            },false);
        },
        // 渲染docx
        docxRender(buffer) {
            let bodyContainer = document.getElementById("bodyContainer");
            renderAsync(
                buffer, // Blob | ArrayBuffer | Uint8Array, 可以是 JSZip.loadAsync 支持的任何类型
                bodyContainer, // HTMLElement 渲染文档内容的元素,
                null, // HTMLElement, 用于呈现文档样式、数字、字体的元素。如果为 null,则将使用 bodyContainer。
                this.docxOptions // 配置
            ).then(res => {
                console.log("res---->", res)
            })
        }
    },
};
</script>

mammoth

使用第三方插件

vue-office