uni-app开发app端pdf.js展示文件流

需求背景

有些PDF文件想要可以在app中查看预览,并且可以分享到微信,但是又不想让人直接获得文件存储地址,所以才用了文件流形式返回数据,(属实搞的焦头烂额)话不多说我们直接开始。

引入PDF.js插件

PDF.js - 入门

点击上面网址打开后选择右侧下载

下载完成后解压会得到web和build两个文件夹,在项目static目录下新建pdf文件夹并把解压后的两个文件夹放入启动。文件路径如图

代码

首先我们要明白pdf.js并不是能直接展示文件流,也就是arrayBuffer,所以我们需要把

arrayBuffer→blob→blob临时路径才可以让pdf.js正常使用,但是在数据层是无法使用一些webAPI的,所有有一部分操作是在视图层完成,也就是uni-app中的renderjs。

renderjs

renderjs | uni-app官网

1.viewer.mjs

首先打开/static/pdf/web/viewer.mjs文件,搜索defaultOptions.defaultUrl

将其中的value值修改为空(修改默认值防止展示原有缺省pdf)

然后再搜索file origin does not match viewer,找到后将判断注释(允许PDF.js跨域)

2.index.vue

<template>
  <view class="dissertation-detail">
    <view class="dissertation-content">
      <web-view :src="pdfUrl" :webview-styles="webviewStyles"></web-view>
    </view>
    <view style="display: none">
      <input @click="pdfRenderjs.emitData" :propFile="propFile" :change:propFile="pdfRenderjs.getPDF"></input>
    </view>
  </view>
</template>

<script>

let wv
export default {
  data() {
    return {
      propFile: '',
      viewerUrl: '/static/pdf/web/viewer.html?file=',//刚解压的文件地址,用来渲染PDF的html
      webviewStyles: {
        width: '95%',
        position: 'relative',
        left: '2.5%'
      },
      pageForm: {} // 请求接口参数
    };
  },
  onReady() {
    // 获取状态栏高度和pdf的高度
    // #ifdef APP-PLUS
    let currentWebview = this.$scope.$getAppWebview() //此对象相当于html5plus里的plus.webview.currentWebview()。在uni-app里vue页面直接使用plus.webview.currentWebview()无效
    setTimeout(function () {
      let height = uni.getSystemInfoSync().screenHeight
      let top = uni.getSystemInfoSync().statusBarHeight
      height = height - top
      wv = currentWebview.children()[0]
      wv.setStyle({ top, height })
    }, 300); //如果是页面初始化调用时,需要延时一下
    // #endif
  },
  async onLoad() {
    // 获取pdf文件流详情
    await this.getPdfDetailBuffer()
  },
  methods: {
    // 获取pdf内容
    async getPdfDetailBuffer() {
      const systemInfo = uni.getSystemInfoSync();
      if(systemInfo.platform === 'ios'){
        this.iosPropFileChange()
      }else if(systemInfo.platform === 'android'){
        this.androidPropFileChange()
      }
    },
    // ios:pdf文件链接
    iosPropFileChange(){
      let _this = this
      uni.request({
        url: `你的请求地址`,
        method: 'GET',
        success(res) {
          _this.propFile = {
            pageForm: _this.pageForm,
            pdf: res.data
          }
        },
        fail(err) {
          console.log(err, '网络错误,或者接口域名错误!')
        }
      })
    },
    // android:pdf文件流
    androidPropFileChange(){
      this.propFile = {
        pageForm: this.pageForm
      }
    },
    
    // 接收视图层数据方法
    receiveRenderData(val) {
      const systemInfo = uni.getSystemInfoSync();
      if(systemInfo.platform === 'ios'){
        this.pdfUrl = decodeURIComponent(val)
      }else if(systemInfo.platform === 'android'){
        this.pdfUrl = this.viewerUrl + val
      }
    },
  },
}
</script>

<script module="pdfRenderjs" lang="renderjs">
export default {
    data() {
        return {
            pdfData: '',
            flag: true
        }
    },
    mounted() {},
    methods: { //选取的文件上传
        getPDF(newValue, oldValue, ownerInstance, instance) {
          const systemInfo = uni.getSystemInfoSync();
          if(systemInfo.platform === 'ios'){
            this.getIosPDF(newValue, oldValue, ownerInstance, instance)
          }else if(systemInfo.platform === 'android'){
            this.getAndroidPDF(newValue, oldValue, ownerInstance, instance)
          }
        },
        // 发送数据到数据层方法
        emitData(e, ownerInstance) {
            this.$ownerInstance.callMethod('receiveRenderData', this.pdfUrl)
        },

        // ios处理方式
        getIosPDF(newValue, oldValue, ownerInstance, instance){
          if (!newValue || !newValue.pageForm.id) return
          if (!this.flag) return false
          this.flag = false
          // 获取pdf文件流详情
          let _this = this
          const id = newValue.pageForm.id
          const pdf = newValue.pdf
          this.pdfUrl = encodeURIComponent(pdf)
          this.emitData()
        },
        // android处理方式
        getAndroidPDF(newValue, oldValue, ownerInstance, instance){
          if (!newValue || !newValue.pageForm.id) return
          if (!this.flag) return false
          this.flag = false
          // 获取pdf文件流详情
          const id = newValue.pageForm.id

          let _this = this
          const header = {
            'Content-Type': 'application/json'
          };
          fetch('你的请求地址', {
            method: 'GET',
            headers: header
          })
          .then(response => response.arrayBuffer()) // 返回结果转为 ArrayBuffer
          .then(pdfData => {
              try {
                  // 尝试将响应数据解析为 JSON
                  const decoder = new TextDecoder('utf-8');
                  const strData = decoder.decode(pdfData);
                  const jsonData = JSON.parse(strData);
                  _this.pageForm.errMsg = jsonData.msg
                  // 如果成功解析为 JSON,则认为是错误信息
              } catch (error) {
                  // 如果解析失败,则认为是 PDF 文件流并转为blob
                  let blob = new Blob([pdfData], {
                      type: 'application/pdf'
                  });
                  let pdfUrl = URL.createObjectURL(blob);
                  _this.pdfUrl = encodeURIComponent(pdfUrl);
                  console.log(_this.pdfUrl, '_this.pdfUrl');
                  _this.emitData();
              }
          })
          .catch(err => {
              console.log(err, '网络错误,或者接口域名错误!');
          });
        }
    }
}
</script>

踩坑

希望我踩的坑能帮助到你

1.app在数据层不存在一些相应的webAPI,所以需要借助renderjs来处理文件流。
2.在ios无论怎么修改我都无法使用fetch来请求成功,所以我只能在数据层请求后把文件流(blob)传给视图层处理。
3.通过数据层请求好文件流(ArrayBuffer)然后传给视图层,但是在视图层转换blob的时候会出现无地址的问题,这也是为什么返回数据ios(blob)和安卓(ArrayBuffer)两个不同的原因。(其实一个也可以,但是时间着急为了求稳安卓就用了已经走通过的方式展示了)
4.高度和webStyle的设置一定要在onReady内执行,在其他生命周期执行会导致获取的高度有误。

本文作者:异·类

本文链接:https://www.cnblogs.com/yilei-zero/p/18650127

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @ 2025-01-06 14:40  异·类  阅读(550)  评论(0编辑  收藏  举报