盆儿 發表於 2025-1-6 14:40:00

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

<h2 id="需求背景">需求背景</h2>
<p>有些PDF文件想要可以在app中查看预览,并且可以分享到微信,但是又不想让人直接获得文件存储地址,所以才用了文件流形式返回数据,(属实搞的焦头烂额)话不多说我们直接开始。</p>
<p><span style="color: rgba(255, 0, 0, 1)"></span></p>
<h2 id="引入pdfjs插件">引入PDF.js插件</h2>
<p>PDF.js - 入门</p>
<p>点击上面网址打开后选择右侧下载</p>
<p><img src="https://img2024.cnblogs.com/blog/1917308/202501/1917308-20250103150926572-295079286.png" alt="" loading="lazy"></p>
<p>下载完成后解压会得到web和build两个文件夹,在项目static目录下新建pdf文件夹并把解压后的两个文件夹放入启动。文件路径如图</p>
<p><img src="https://img2024.cnblogs.com/blog/1917308/202501/1917308-20250103150947345-1295209491.png" alt="" loading="lazy"></p>
<h2 id="代码">代码</h2>
<p>首先我们要明白pdf.js并不是能直接展示文件流,也就是arrayBuffer,所以我们需要把</p>
<p>arrayBuffer→blob→blob临时路径才可以让pdf.js正常使用,但是在数据层是无法使用一些webAPI的,所有有一部分操作是在视图层完成,也就是uni-app中的renderjs。</p>
<p>renderjs</p>
<p>renderjs | uni-app官网</p>
<h3 id="1viewermjs">1.viewer.mjs</h3>
<p>首先打开/static/pdf/web/viewer.mjs文件,搜索defaultOptions.defaultUrl</p>
<p><img src="https://img2024.cnblogs.com/blog/1917308/202501/1917308-20250103151003951-101690311.png" alt="" loading="lazy"></p>
<p>将其中的value值修改为空(修改默认值防止展示原有缺省pdf)</p>
<p><img src="https://img2024.cnblogs.com/blog/1917308/202501/1917308-20250103151012803-1124146334.png" alt="" loading="lazy"></p>
<p>然后再搜索file origin does not match viewer,找到后将判断注释(允许PDF.js跨域)</p>
<p><img src="https://img2024.cnblogs.com/blog/1917308/202501/1917308-20250103151020040-1052756104.png" alt="" loading="lazy"></p>
<h3 id="2indexvue">2.index.vue</h3>
<pre><code>&lt;template&gt;
&lt;view class="dissertation-detail"&gt;
    &lt;view class="dissertation-content"&gt;
      &lt;web-view :src="pdfUrl" :webview-styles="webviewStyles"&gt;&lt;/web-view&gt;
    &lt;/view&gt;
    &lt;view style="display: none"&gt;
      &lt;input @click="pdfRenderjs.emitData" :propFile="propFile" :change:propFile="pdfRenderjs.getPDF"&gt;&lt;/input&gt;
    &lt;/view&gt;
&lt;/view&gt;
&lt;/template&gt;

&lt;script&gt;

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()
      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
      }
    },
},
}
&lt;/script&gt;

&lt;script module="pdfRenderjs" lang="renderjs"&gt;
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 =&gt; response.arrayBuffer()) // 返回结果转为 ArrayBuffer
          .then(pdfData =&gt; {
            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(, {
                      type: 'application/pdf'
                  });
                  let pdfUrl = URL.createObjectURL(blob);
                  _this.pdfUrl = encodeURIComponent(pdfUrl);
                  console.log(_this.pdfUrl, '_this.pdfUrl');
                  _this.emitData();
            }
          })
          .catch(err =&gt; {
            console.log(err, '网络错误,或者接口域名错误!');
          });
      }
    }
}
&lt;/script&gt;

</code></pre>
<h2 id="踩坑">踩坑</h2>
<p>希望我踩的坑能帮助到你</p>
<p>1.app在数据层不存在一些相应的webAPI,所以需要借助renderjs来处理文件流。<br>
2.在ios无论怎么修改我都无法使用fetch来请求成功,所以我只能在数据层请求后把文件流(blob)传给视图层处理。<br>
3.通过数据层请求好文件流(ArrayBuffer)然后传给视图层,但是在视图层转换blob的时候会出现无地址的问题,这也是为什么返回数据ios(blob)和安卓(ArrayBuffer)两个不同的原因。(其实一个也可以,但是时间着急为了求稳安卓就用了已经走通过的方式展示了)<br>
4.高度和webStyle的设置一定要在onReady内执行,在其他生命周期执行会导致获取的高度有误。</p><br><br>
来源:https://www.cnblogs.com/yilei-zero/p/18650127
頁: [1]
查看完整版本: uni-app开发app端pdf.js展示文件流