解决Android的WebView无法打开PDF的方案

2023-07-12 00:56:40 浏览数 (2)

背景

最近自家产品开发使用中收到反馈,安卓内嵌网页无法打开PDF,而IOS可以打开。其实安卓无法打开分以下几种情况:

  • 有.pdf后缀但是文档比较小的——可以打开
  • 有.pdf后缀但是文档比较大的——无法打开
  • 无.pdf后缀的——都打不开

原因分析

Android的内置WebView引擎 主要用于显示网页内容和基本的文本渲染 ,对于复杂的多媒体和特定文件类型的渲染支持有限, 不直接支持打开大型的带有.pdf后缀的PDF文件预览对于不具有.pdf后缀的PDF文件,WebView默认将其视为普通的文本或二进制文件,因此无法直接预览

IOS的WebView使用的是 WebKit引擎 ,该引擎内置了对PDF文件的支持, 可以直接预览和展示PDF内容 。所以IOS的WebView可以打开带有.pdf后缀的文件,并提供内置的PDF查看器,使用户可以直接再应用中查看PDF文件。 对于没有.pdf后缀的文件,IOS仍可以预览并打开,因为IOS系统的预览功能会自动识别文件类型,并使用适当的预览器打开文件

这里我主要针对无.pdf后缀的,H5利用Canvas和PDF.js,给一下我开发时的解决方法。不过我也会将我收集到的安卓的方案写这里,不过我不是安卓开发工程师,欢迎补充。

解决方案

安卓方案

  1. 使用特定的PDF库:Android上有许多为PDF预览和操作提供支持的 第三方库 ,如Adobe PDF Library和MuPDF等。这些库可以在WebView中渲染和打开PDF文件。
  2. 使用其他应用程序:(通过使用Inteynt机制?),可以调用系统中预装的PDF阅读器应用程序或其他支持PDF的应用程序来打开和预览PDF文件。可以根据文件类型和URI来指定打开PDF文件的应用程序。例如,使用封装pdf的应用程序,如Adobe Acrobat Reader或其他PDF阅读器应用程序

H5使用pdf.js利用Canvas将页面渲染出来

  1. 引入PDF.js(注意如果要下载下来使用的化, pdf.worker.min.js 也要下载下来) <script src="https://unpkg.com/pdfjs-dist@1.9.426/build/pdf.min.js"></script>
  2. 主要JS代码
代码语言:html复制
 <script>
    // 获取页面参数
    // 测试:?pdf=https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf
    let url = new URLSearchParams(window.location.search).get("pdf");
    let pdfDoc = null;

    function createPage() {
      let div = document.createElement("canvas");
      document.body.appendChild(div);
      return div;
    }

    function renderPage(num) {
      pdfDoc.getPage(num).then(function (page) {
        let viewport = page.getViewport(2.0);
        let canvas = createPage();
        let ctx = canvas.getContext('2d');

        canvas.height = viewport.height;
        canvas.width = viewport.width;

        page.render({
          canvasContext: ctx,
          viewport: viewport
        });
      });
    }

    // 如果是由后端的pdf链接地址,且跨域需要携带cookie验证,getDocument中传{ url: url, withCredentials: true }
    PDFJS.getDocument({ url: url }).then(function (pdf) {
      pdfDoc = pdf;

      for (let i = 1; i <= pdfDoc.numPages; i  ) {
        renderPage(i)
      }
    })
  </script>

附整个页面的代码

代码语言:html复制
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport"
    content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=4.0, user-scalable=no">
  <title>安卓PDF阅读页面</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    canvas {
      width: 100%;
      height: 100%;
      border: 1px solid rgb(221, 221, 221);
    }

    /* 加载中的转圈圈样式 */
    .spinner-box {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      z-index: 2;
      background-color: rgba(0, 0, 0, 0.3);
    }

    .spinner-box img {
      width: 9.3333vw;
      height: 9.3333vw;
    }

    .spinner-box .title {
      margin: 2.6667vw 0 0;
      font-size: 3.2vw;
      color: #fff;
      text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.5);
      white-space: pre-line;
      text-align: center;
    }

    .spinner-box .spinner {
      width: 9.3333vw;
      height: 9.3333vw;
      animation: rotator 1.4s linear infinite;
    }

    @keyframes rotator {
      0% {
        transform: rotate(0deg);
      }

      100% {
        transform: rotate(270deg);
      }
    }

    .spinner-box .path {
      stroke-dasharray: 187;
      stroke-dashoffset: 0;
      transform-origin: center;
      animation: dash 1.4s ease-in-out infinite, colors 5.6s ease-in-out infinite;
    }

    @keyframes colors {
      0% {
        stroke: #fff;
      }

      25% {
        stroke: #f7e7d7;
      }

      50% {
        stroke: #eecfaf;
      }

      75% {
        stroke: #e5b788;
      }

      100% {
        stroke: #dd9f60;
      }
    }

    @keyframes dash {
      0% {
        stroke-dashoffset: 187;
      }

      50% {
        stroke-dashoffset: 46.75;
        transform: rotate(135deg);
      }

      100% {
        stroke-dashoffset: 187;
        transform: rotate(450deg);
      }
    }
  </style>

</head>
<script src="https://unpkg.com/pdfjs-dist@1.9.426/build/pdf.min.js"></script>
<body>
  <div id="loading" class="spinner-box">
    <!-- 上传中 -->
    <svg class="spinner" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
      <circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
    </svg>
  </div>

  <script>
    // 获取页面参数
    // 测试:?pdf=https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf
    let url = new URLSearchParams(window.location.search).get("pdf");
    let pdfDoc = null;

    function createPage() {
      let div = document.createElement("canvas");
      document.body.appendChild(div);
      return div;
    }

    function renderPage(num) {
      pdfDoc.getPage(num).then(function (page) {
        let viewport = page.getViewport(2.0);
        let canvas = createPage();
        let ctx = canvas.getContext('2d');

        canvas.height = viewport.height;
        canvas.width = viewport.width;

        page.render({
          canvasContext: ctx,
          viewport: viewport
        });
      });
    }

    let loading = document.getElementById("loading")

    // 显示加载中
    loading.style.display = "flex";

    // 如果是由后端的pdf链接地址,且跨域需要携带cookie验证,getDocument中传{ url: url, withCredentials: true }
    PDFJS.getDocument({ url: url }).then(function (pdf) {
      pdfDoc = pdf;

      for (let i = 1; i <= pdfDoc.numPages; i  ) {
        renderPage(i)
      }
      // 隐藏加载中
      loading.style.display = "none";
    })
  </script>
</body>
</html>

如有不足,欢迎指正

0 人点赞