• 一行代码优化 pdfjs 加载大文件的pdf 速度


    介绍

    先简单介绍下pdfjs 怎么 去加载pdf文件

    import * as PDFJS from 'pdfjs-dist/legacy/build/pdf'
    PDFJS.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/legacy/build/pdf.worker.entry.js')
    
    // blobUrl container指 dom 承载pdf 的容器
    export const loadAndDisplayPdfByBlobUrl = (blobUrl, container) => {
      // 加载PDF文件
      PDFJS.getDocument(blobUrl).promise.then(async (pdf) => {
        const totalPages = pdf.numPages
    
        // 循环绘制每个页面 
        for (let pageNum = 1; pageNum <= totalPages; pageNum++) {
          let page = await pdf.getPage(pageNum)
          const viewport = page.getViewport({ scale: 1 })
          const canvas = createCanvasDom(viewport.width, viewport.height)
          container.appendChild(canvas)
          const context = canvas.getContext('2d')
    
          // 将每一页pdf内容 渲染页面到canvas
          await page.render({
            canvasContext: context,
            viewport: viewport,
          })
        }
      })
    }
    
    const createCanvasDom = (width, height) => {
      const canvas = document.createElement('canvas')
      // 设置canvas的宽度和高度
      canvas.width = width
      canvas.height = height
      return canvas
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    简单 页面使用 (vue2 举例)

    <template>
      <div>
        // 为了测试 自己上传pdf 
        <Uploader :after-read="afterRead" accept="*"></Uploader>
        <div id="pdf"></div>
      </div>
    </template>
    
    <script>
    import { loadAndDisplayPdfByBlobUrl } from './handel'
    import { Uploader } from 'vant'
    
    export default {
      components: {
        Uploader,
      },
      data() {
        return {
          fileUrl: '',
        }
      },
    
      mounted() {
        loadAndDisplayPdfByBlobUrl()
      },
      methods: {
        afterRead(file) {
          // 将 file 对象 转化成 blobUrl 
          this.fileUrl = URL.createObjectURL(file.file)
          loadAndDisplayPdfByBlobUrl(this.fileUrl, document.querySelector('#pdf'))
        },
      },
    }
    </script>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    问题

    上面的写法 处理体积小的 pdf文件 不会出现啥问题

    当文件过大的时候 渲染量过大 很长一段时间界面会出现白屏 。用户体验不好

    如下图:

    在这里插入图片描述
    这个js 忍者秘籍pdf 400多页 ,从上传后到 页面出现结果 消耗了大约 11s

    全网最多的解决方案就是 传入的fileUrl 支持 分片下载,在开启 disableRange

    export function getDocument(src: GetDocumentParameters): PDFDocumentLoadingTask;
    
    export type GetDocumentParameters = string | URL | TypedArray | ArrayBuffer | PDFDataRangeTransport | DocumentInitParameters;
    
    // 在 DocumentInitParameters 类型中有个 属性
    /**
         * - Disable range request loading of PDF
         * files. When enabled, and if the server supports partial content requests,
         * then the PDF will be fetched in chunks. The default value is `false`.
         */
        disableRange?: boolean | undefined;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    PDFJS.getDocument(url, {
      disableRange: true
    }).then(function(pdfDocument) {
      // 处理 PDF 文档
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    还需要后端改动 ,还是前端自己来吧

    分析

    在这里插入图片描述
    会发现 微任务 执行耗时太久 , 页面 没有发生render

    这涉及到 一个 队列优先级的问题,粗略说下

    在这里插入图片描述
    优先级一般都是从上到下

    我们可以得出结论 :
    微任务执行 阻塞了 渲染(每一次微任务执行后 会创建下一页渲染的微任务 渲染进程 的 渲染主线程 一直会执行微任务队列里面的任务 会被一直占用)

    解决

    我们可以考虑在每一页 渲染的时候 (微任务)中间插入一个 比渲染队列低的任务 空出时间给 渲染主线程 去执行 渲染队列的 任务

    可以 创建一个 延时队列的任务 主进程会执行渲染队列任务 后在执行延时队列任务
    等待延时,后再创建 渲染pdf下一页的微任务。反复如此执行

    最终代码如下

    import * as PDFJS from 'pdfjs-dist/legacy/build/pdf'
    PDFJS.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/legacy/build/pdf.worker.entry.js')
    
    export const loadAndDisplayPdfByBlobUrl = (blobUrl, container) => {
      // 加载PDF文件
      PDFJS.getDocument(blobUrl).promise.then(async (pdf) => {
        const totalPages = pdf.numPages
    
        // 循环绘制每个页面
        for (let pageNum = 1; pageNum <= totalPages; pageNum++) {
          let page = await pdf.getPage(pageNum)
          const viewport = page.getViewport({ scale: 1 })
          const canvas = createCanvasDom(viewport.width, viewport.height)
          container.appendChild(canvas)
          const context = canvas.getContext('2d')
    
          // 渲染页面到canvas
          await page.render({
            canvasContext: context,
            viewport: viewport,
          })
          // 下一页 渲染 前创建 延时队列任务 延时时间可以自己调整 这里为了测试效果写了100ms 一般 100ms 效果就很OK了
          await sleep(100)
        }
      })
    }
    
    const createCanvasDom = (width, height) => {
      const canvas = document.createElement('canvas')
      // 设置canvas的宽度和高度
      canvas.width = width
      canvas.height = height
      return canvas
    }
    
    const sleep = (time) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(time)
        }, time)
      })
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    优化后效果如下:

    在这里插入图片描述
    2s 左右 pdf 第一页就出来了

    后面基本 间隔 等待时间 后渲染下一页 表现如下:
    在这里插入图片描述

    结束

    优化后 总渲染时间会变长 ,交互效果会更好

    后面抽时间总结一篇 浏览器事件循环 的文章

  • 相关阅读:
    IP 协议的相关特性
    机器学习(三十四):可视化决策树的四个方法
    oracle执行计划中,同一条语句块,在不同情况下执行计划不一样问题。子查询,union 导致索引失效。
    tidymodels绘制校准曲线
    C++ 多态
    .NET 6学习笔记(7)——ASP.NET Core通过配置文件启用HTTPS
    tez作业运行慢
    Kubernetes 应用容器化
    Plurality in Voxel——VoxEdit 比赛
    PHP 图像处理组件:Intervention/image
  • 原文地址:https://blog.csdn.net/weixin_45485922/article/details/133812353