之前手里好几个项目都需要用到pdf预览,在网上收罗了一大堆资料,最终选定了pdf.js。

原因:

  不用pdf.js的话,就需要去安装各种环境的pdf插件,比如vue环境的vue-pdf,虽然可以自定义部分功能,但是除了它的展示,其他功能需要自己去添加(略麻烦),最不能忍的是不兼容ie!!!(看其他文章有适配ie的,不知道是不是博主环境版本原因,实在是显示不了),对于angular环境,也没找到适用的插件。

  pdf.js虽然只能兼容到ie10+,但现在都什么时代了,使用低版本浏览器的用户毕竟占少数。而且它好歹是原生的,可以适用任何框架(jquery、vue及angular等),只要配置好一份插件,就可吃遍天下的既视感。

 

1.下载

参照链接:

博主用的链接文章中提到的第一种方法(使用自带的viewer.html预览)

插件存放路径:原生框架可以放置任何位置,文中就放置于common/pdf路径,vue与angular框架就需要放置于静态资源文件static/pdf路径

2.更改配置文件

参照链接:

(1)修改view.js(找到以下内容加以更改)

// var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf'; '' //注释默认

...

//注释对于是否允许跨域的判断
// var viewerOrigin = new URL(window.location.href).origin || 'null';
// if (HOSTED_VIEWER_ORIGINS.indexOf(viewerOrigin) >= 0) {
//   return;
// }
// var fileOrigin = new URL(file, window.location.href).origin;
// if (fileOrigin !== viewerOrigin) {
//   throw new Error('file origin does not match viewer\'s');
// }

//当直接打开base64时,file是个对象,而不是string类型的url,所以此处需要判断

  if (file && typeof file =='string') {

      var fileOrigin = new URL(file, window.location.href).origin;

  }

 

...

 webViewerOpenFileViaURL = function webViewerOpenFileViaURL(file) {

//增加判断file是string

    if (file && typeof file=='string' && file.lastIndexOf('file:', 0) === 0) {

      PDFViewerApplication.setTitleUsingUrl(file);

      var xhr = new XMLHttpRequest();

      xhr.onload = function () {

        PDFViewerApplication.open(new Uint8Array(xhr.response));

      };

      try {

        xhr.open('GET', file);

        xhr.responseType = 'arraybuffer';

        xhr.send();

      } catch (ex) {

        PDFViewerApplication.l10n.get('loading_error', null, 'An error occurred while loading the PDF.').then(function (msg) {

          PDFViewerApplication.error(msg, ex);

        });

      }

      return;

    }

    if (file) {

      PDFViewerApplication.open(file);

    }

  };

(2)修改view.html(下段代码需放置于view.js文件引用之前)

上述链接的文中是用sessionStorage来存储base64,会有一个弊端,如果base64太长,数据过大,会超出sessionStorage的存储上限,这时候就会去想有没有办法扩大sessionStorage的存储容量,博主找了一些方法,经测试之后都不能有效解决这个问题。最终选择了一个稳妥的方式:用iframe嵌套(将数据存储于iframe层某个dom元素里,获取数据时直接在view.html层读取父级dom元素的数据即可,就不用大费周章的去想怎么存储数据的问题),在引用的时候会把具体代码呈现。

<script type="text/javascript">
  var DEFAULT_URL = "";
  var pdfUrl = document.location.search.substring(1);
  if (null == pdfUrl || "" == pdfUrl) {
    var BASE64_MARKER = ';base64,'; //声明文件流编码格式
    var preFileId = "";
    var pdfAsDataUri = window.top.document.getElementById('baseUrl').innerHTML; //获取父级dom存储的数据
    var pdfAsArray = convertDataURIToBinary(pdfAsDataUri);
    DEFAULT_URL = pdfAsArray;
    //编码转换
    function convertDataURIToBinary(dataURI) {
      //[RFC2045]中有规定:Base64一行不能超过76字符,超过则添加回车换行符。因此需要把base64字段中的换行符,回车符给去掉。
      var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
      var newUrl = dataURI.substring(base64Index).replace(/[\n\r]/g, '');
      var raw = window.atob(newUrl); //这个方法在ie内核下无法正常解析。
      var rawLength = raw.length;
      //转换成pdf.js能直接解析的Uint8Array类型
      var array = new Uint8Array(new ArrayBuffer(rawLength));
      for (i = 0; i < rawLength; i++) {
        array[i] = raw.charCodeAt(i) & 0xff;
      }
      return array;
    }
  }
</script>

3.引用(打开一个新窗口,以iframe的形式嵌套view.html)

文中数据来源是接口获取,它可能是base64格式,也可能是文件流,博主下文都会给出具体示例。(接口需要的参数传至下文提到的新页面,接口也在该处调用,怎么打开新页面并传参就不用博主多讲了。。)

(1)原生框架

新建pdf_view.html

html部分

<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv=Content-Type content="text/html; charset=utf-8">
    <title></title>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <style>
      html,body, #content{height:100%;}
       body{margin:0;overflow:hidden;}
       #content{
         text-align:center;
         background:rgba(0,0,0,.6);
       }
       img{
         max-width:100%;
         max-height:100%;
       }
    </style>
</head>
<body>
<div id="baseUrl" hidden></div> <!--存储base64数据-->
<div id="content"></div> <!--动态渲染iframe-->
</body>
</html>

script部分:

var common = require("common");
    var url = common.Url.resumeApiHost;
    var fileId = common.getQueryString('fileId');
    var fileName = common.getQueryString('fileName');
    document.title = fileName;  
    //获取文件流
    function getFileBlob() {
      var header = {};
      header['Authorization'] = 'cmbnthr ' + common.Cookies.getCookie("userToken");
      new common.ajaxRequest({
          url: url  + "cmbntResume/attachment/view/"+fileId,
          type: "GET",
          param: '',
          header: header,
          callBack: function (data) {if(IEVersion()<10 && IEVersion()!=-1){
              common.Common.jAlert("请更新您的浏览器!");
              return;
            }
            $('#baseUrl').html('data:application/pdf;base64,'+data.data)
            $('#content').html('<iframe id="iframe" src="../../pdf/web/viewer.html" frameborder="0" width="100%" height="100%" border="0"></iframe>')
          }
      })
  }
  getFileBlob();
  //IE版本判断
  function IEVersion() {
    var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
    var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1; //判断是否IE<11浏览器
    var isEdge = userAgent.indexOf("Edge") > -1 && !isIE; //判断是否IE的Edge浏览器
    var isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf("rv:11.0") > -1;
    if (isIE) {
        var reIE = new RegExp("MSIE (\\d+\\.\\d+);");
        reIE.test(userAgent);
        var fIEVersion = parseFloat(RegExp["$1"]);
        if (fIEVersion == 7) {
            return 7;
        } else if (fIEVersion == 8) {
            return 8;
        } else if (fIEVersion == 9) {
            return 9;
        } else if (fIEVersion == 10) {
            return 10;
        } else {
            return 6;//IE版本<=7
        }
    } else if (isEdge) {
        return 'edge';//edge
    } else if (isIE11) {
        return 11; //IE11
    } else {
        return -1;//不是ie浏览器
    }
  }

(2)vue框架

新建pdfView.vue

<template>
  <div>
    <div id="baseUrl" hidden>{{pdfData}}</div>
    <iframe id="iframes" v-if="pdfData" :src="pdfUrl" frameborder="0" width="100%" height="100%"></iframe>
  </div>
</template>

<script>
  import { downloadFileBlob } from '@/api/checkAppointment/request'
  export default {
    data(){
      return{
        pdfUrl: '',
        pdfData: ''
      }
    },
    created(){
      downloadFileBlob({fileIDName: window.location.hash.split('=')[1] || ''}).then(res => {
    //这里返回的是文件流,把它转换成base64格式
        const blob = new Blob([res])
        const _this = this
        var reader = new window.FileReader();
        reader.readAsDataURL(blob); 
        reader.onloadend = function() {
          _this.pdfData = reader.result;
          _this.pdfUrl="/static/pdf/web/viewer.html"
        }
      })
    },
    methods:{

    }
  }
</script>

<style lang="scss" scoped>
iframe{
  height:calc(100vh - 20px)
}
</style>

其他框架同理,如有问题,欢迎评论!