【uniapp】App平台展示pdf文件

ios

ios比较简单,可以直接使用如下代码。

let url = 'xxx.com/xx.pdf'
uni.downloadFile({
          url: url,
          success: function (res) {
            var filePath = res.tempFilePath;
            uni.openDocument({
              filePath: filePath,
              success: function (res) {
                console.log('打开文档成功');
              }
            });
          }
        });

安卓

安卓如果没有可以打开的应用,将没有反应。
主流程:使用webview,内嵌pdfjs。

下载pdfjs

下载网址:https://mozilla.github.io/pdf.js/getting_started/#download
注意:安卓的话建议下载legacy的版本,否则一些api可能有兼容问题.
常见的错误有:Promise.withResolver() is not a functionAbortSignal.any is not a functioninvalid regular expression flags等。

浏览器支持:https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#which-browsersenvironments-are-supported

如果需要兼容更低的版本,参考更多版本支持:https://github.com/mozilla/pdf.js/releases

uniapp的web-view组件文档:https://uniapp.dcloud.net.cn/component/web-view.html#app%E7%AB%AFweb-view%E7%9A%84%E6%89%A9%E5%B1%95

新建pdfPreview.vue

直接使用pdfjs提供的viewer.html

参考如下。url为http://xxx.com/xx.pdf。此处需要获取webview并设置style,否则进入页面后,会占用状态栏和导航栏。

<template>
  <web-view :src="webViewUrl"></web-view>
</template>

<script>
export default {
  data() {
    return {
      pdfUrl: '', // 原始PDF地址
      webViewUrl: '', // 本地HTML页面地址
      title: '',
    };
  },
  onLoad(options) {
    this.pdfUrl = options.url;
    this.title = options.title
    this.initWebView();
    let height = 0; //定义动态的高度变量
    let statusbar = 0; // 动态状态栏高度
    uni.getSystemInfo({
      // 获取当前设备的具体信息
      success: (sysinfo) => {
        statusbar = sysinfo.statusBarHeight;
        height = sysinfo.windowHeight;

        let currentWebview = this.$scope.$getAppWebview(); //获取当前web-view
        setTimeout(function () {
          var wv = currentWebview.children()[0];
          wv.setStyle({
            //设置web-view距离顶部的距离以及自己的高度,单位为px
            top: statusbar + 44, //此处是距离顶部的高度,应该是你页面的头部
            height: height - statusbar //webview的高度
          });
        }, 200); //如页面初始化调用需要写延迟
      }
    });
  },
  methods: {
    initWebView() {
      this.webViewUrl = `/hybrid/html/local.html?title=${this.title}&pdfUrl=${encodeURIComponent(this.pdfUrl)}`;
    }
  }
};
</script>

<style scoped></style>

hybrid/html

新建local.html如下:

<!DOCTYPE html>

<html>
  <head>
    <meta name="viewport"
      content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <title>PDF预览</title>
    <style>
      html {
        height: 100%;
        width: 100%;

        margin: 0;
        padding: 0;
      }

      body {
        margin: 0;
        padding: 0;
      }
    </style>
  </head>

  <body>
    <div id="url"></div>
    <iframe id="iframe" src="" width="100%" height="100vh" style="height: 100vh;width:100vw;border:none;"></iframe>


    <script>
      document.addEventListener("DOMContentLoaded", function() {
        const urlParams = new URLSearchParams(window.location.search);
        url = urlParams.get("pdfUrl");
        // document.getElementById('url').innerHTML = url;
        document.getElementById('iframe').src = 'pdfjs/web/viewer.html?file=' + encodeURIComponent(url)
        const title = urlParams.get("title");
        if (title) {
          document.title = title;
        }
      });
    </script>
  </body>
</html>

把下载好的pdfjs解压,放到hybrid/html下,
可以看到这里的src开头为pdfjs/web/viewer.html,也就是pdfjs写好的预览网页。
如果有报错file origin does not match viewer's,可以修改:
pdfjs\web\viewer.js 1614行-1616行:

      // if (fileOrigin !== viewerOrigin) {
      //   throw new Error("file origin does not match viewer's");
      // }

pdfjs\web\viewer.mjs 22403行:

if (fileOrigin === viewerOrigin || fileOrigin !== viewerOrigin) {
      return;
    }

不同的版本可能行号不太一样,搜索file origin看下出现的地方并修改就行。

不使用pdfjs提供的viewer,而是自己写

下面是一个简单的demo:(更多demo可以参考官网

<!DOCTYPE html>

<html>
  <head>
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>PDF预览</title>
    <style>
      html {
        height: 100%;
        width: 100%;
      }

      .flex {
        display: flex;
      }
      .justify-between {
        justify-content: space-between;
      }
      .gap-10 {
        gap: 10px;
      }
      .items-center {
        align-items: center;
      }

      #the-canvas {
        margin-top: 20px;
        border: 1px solid grey;
        direction: ltr;
      }
      #loading {
        position: absolute;
        top: 30%;
        left: calc(50% - 15px);
        width: 30px;
        height: 30px;
        border: 2px solid #eee;
        border-top-color: #007aff;
        border-radius: 50%;
        animation: spin 1s linear infinite;
      }
      @keyframes spin {
        from {
          transform: rotate(0deg);
        }
        to {
          transform: rotate(360deg);
        }
      }
    </style>
  </head>

  <body>
    <div class="flex items-center justify-between">
      <div>
        <button id="enlarge">放大</button>
        <button id="reduce">缩小</button>
      </div>
      <div class="flex items-center gap-10">
        <span
          >Page: <span id="page_num"></span> / <span id="page_count"></span
        ></span>
        <button id="prev">上一页</button>
        <button id="next">下一页</button>
      </div>
    </div>
    <div id="loading"></div>
    <div id="error" style="color:red;"></div>
    <div id="canvas-block" style="display: none">
      <canvas id="the-canvas"></canvas>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.7.107/pdf.js"></script>

    <script>
      document.addEventListener("UniAppJSBridgeReady", function () {});
      pdfjsLib.GlobalWorkerOptions.workerSrc =
        "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.7.107/pdf.worker.js";

      var url = " ",
        pdfDoc = null,
        pageNum = 1,
        pageRendering = false,
        pageNumPending = null,
        scale = 1,
        defaultScale = 1,
        isFirstRender = true,
        canvas = document.getElementById("the-canvas"),
        ctx = canvas.getContext("2d");
      // 指定工作线程脚本的路径

      function renderPage(num) {
        // 加载PDF文档
        pdfDoc.getPage(num).then(function (page) {
          if (isFirstRender) {
            const style = window.getComputedStyle(document.body);
            const contentWidth = parseFloat(style.width);
            var desiredWidth = contentWidth;
            let defaultViewport = page.getViewport({
              scale: 1,
            });
            defaultScale = desiredWidth / defaultViewport.width;
            isFirstRender = false;
          }

          let scaledViewport = page.getViewport({
            scale,
          });
          canvas.width = scaledViewport.width;
          canvas.height = scaledViewport.height;

          let renderContext = {
            canvasContext: ctx,
            viewport: scaledViewport,
          };
          let renderTask = page.render(renderContext);

          // 等待渲染完成
          renderTask.promise.then(function () {
            pageRendering = false;
            document.getElementById("loading").style.display = "none";
            document.getElementById("canvas-block").style.display = "block";
            if (pageNumPending !== null) {
              // New page rendering is pending
              renderPage(pageNumPending);
              pageNumPending = null;
            }
          }, function (reason) {
            document.getElementById("loading").style.display = "none";
            document.getElementById("error").innerHTML = document.getElementById("error").innerHTML + '\n Error: renderTask: ' + reason;
          });

          // Update page counters
          document.getElementById("page_num").textContent = num;
        }).catch(err => {
          document.getElementById("loading").style.display = "none";
          document.getElementById("error").innerHTML = document.getElementById("error").innerHTML + '\n Error: getPage: ' + err;
        });
      }

      function queueRenderPage(num) {
        if (pageRendering) {
          pageNumPending = num;
        } else {
          renderPage(num);
        }
      }

      function onPrevPage() {
        if (pageNum <= 1) {
          return;
        }
        pageNum--;
        queueRenderPage(pageNum);
      }
      document.getElementById("prev").addEventListener("click", onPrevPage);

      function onNextPage() {
        if (pageNum >= pdfDoc.numPages) {
          return;
        }
        pageNum++;
        queueRenderPage(pageNum);
      }
      document.getElementById("next").addEventListener("click", onNextPage);

      function onEnlarge() {
        if (pageRendering) {
          return;
        }
        scale += 0.1;
        queueRenderPage(pageNum);
      }
      document.getElementById("enlarge").addEventListener("click", onEnlarge);

      function onReduce() {
        if (pageRendering) {
          return;
        }
        if (scale <= defaultScale) {
          return; // 防止过度缩小
        }
        scale -= 0.1;
        queueRenderPage(pageNum);
      }
      document.getElementById("reduce").addEventListener("click", onReduce);

      document.addEventListener("DOMContentLoaded", function () {
        const urlParams = new URLSearchParams(window.location.search);
        url = urlParams.get("pdfUrl");
        const title = urlParams.get("title");
        if (title) {
          document.title = title;
        }
        // document.getElementById('url').textContent = url;
        pdfjsLib.getDocument(url).promise.then(function (pdfDoc_) {
          pdfDoc = pdfDoc_;
          document.getElementById("page_count").textContent = pdfDoc.numPages;

          // Initial/first page rendering
          renderPage(pageNum);
        }).catch(err => {
          document.getElementById("loading").style.display = "none";
          document.getElementById("error").innerHTML = document.getElementById("error").innerHTML + '\n Error: getDocument: ' + err;
        });
      });
    </script>
  </body>
</html>

其它注意事项

  1. ios 里使用 pdfjs 将无法显示,就算直接打开pdfjs给的demo也无法展示,暂不清楚原因。
    https://mozilla.github.io/pdf.js/web/viewer.html
    https://mozilla.github.io/pdf.js/legacy/web/viewer.html

有了解的大佬请评论区留言
有任何疑问可以共同交流~

### 三、实现 PDF 文件流预览的方法 在 UniApp 开发 App 时,如果需要预览从后端接口获取的 PDF 文件流,可以通过 `web-view` 组件结合 PDF.js 来实现。由于小程序和 App 端不支持直接渲染 PDF 文件流,因此需要借助 WebView 加载 HTML 页面来展示 PDF 内容。 在页面中使用 `web-view` 组件,并通过 URL 传递 PDF 文件流地址。例如,可以创建一个 `.vue` 文件,配置 `web-view` 组件并动态拼接 URL,实现 PDF 的在线预览功能: ```vue <template> <view style="width: 100%; height: 90vh;"> <web-view :src="allUrl"></web-view> </view> </template> <script> export default { data() { return { allUrl: '', viewerUrl: '/hybrid/html/web/viewer.html' // viewer.html 文件路径 }; }, onLoad(params) { let fileUrl = encodeURIComponent(decodeURIComponent(params.fileUrl)); // 解码传入的 PDF 地址 this.allUrl = this.viewerUrl + '?file=' + fileUrl; // 拼接 PDF 文件路径 } }; </script> ``` 在上述代码中,`viewer.html` 是一个基于 PDF.js 的 HTML 文件,用于加载和渲染 PDF 文件流。通过 `web-view` 组件加载该 HTML 文件,并将实际的 PDF 文件地址作为参数传递给它,即可实现 PDF 的在线预览[^2]。 此外,如果需要从接口获取 PDF 文件流,可以在请求成功后拼接 URL 并赋值给 `web-view`: ```javascript onLoad(params) { uni.showLoading({ title: '加载中' }); this.$http.request({ url: `https://api.example.com/get-pdf`, method: 'post', }).then(res => { this.allUrl = this.viewerUrl + '?file=' + encodeURIComponent(res.data.pdfUrl); uni.hideLoading(); }).catch(() => { uni.hideLoading(); }); } ``` 通过这种方式,可以确保在 App 端(包括 Android 和 iOS)实现 PDF 文件流的预览功能,同时支持缩放、翻页等基本阅读操作[^3]。 ### 三、兼容性与注意事项 - **PDF.js 版本问题**:在使用 PDF.js 时,需要注意版本兼容性问题。某些版本的 PDF.js 在 UniApp 中可能会出现报错,例如 `Setting up fake worker failed: “pattern.at is not a function”`。可以通过更新 PDF.js 版本或修改 worker 配置来解决此类问题[^1]。 - **文件路径问题**:确保 `viewer.html` 和 PDF.js 的路径正确,并且放置在 `hybrid/html/web` 目录下,以便 `web-view` 正确加载。 - **跨域问题**:如果 PDF 文件流来自外部服务器,需确保服务器配置了正确的 CORS 策略,否则可能无法加载 PDF 文件- **性能优化**:PDF 文件较大时,加载速度可能较慢,建议对 PDF 文件进行压缩或分页加载优化。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值