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

news2024/11/19 22:35:56

目录

    • 介绍
    • 问题
    • 分析
    • 解决
    • 结束

介绍

先简单介绍下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
}


简单 页面使用 (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>

问题

上面的写法 处理体积小的 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;

PDFJS.getDocument(url, {
  disableRange: true
}).then(function(pdfDocument) {
  // 处理 PDF 文档
});

还需要后端改动 为了让后端小伙伴安心的摸鱼,还是前端自己来吧

分析

在这里插入图片描述
会发现 微任务 执行耗时太久 , 页面 没有发生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)
  })
}

优化后效果如下:

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

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

结束

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1088749.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4年软件测试,突破不了20K,太卷了。。。

先说一个插曲&#xff1a;上个月我有同学在深圳被裁员了&#xff0c;和我一样都是软件测试&#xff0c;不过他是平安外包&#xff0c;所以整个组都撤了&#xff0c;他工资和我差不多都是14K。 现在IT互联网已经比较寒冬&#xff0c;特别是软件测试&#xff0c;裁员先裁测试&am…

IDEA 修改插件安装位置

不说假话&#xff0c;一定要看到最后&#xff0c;不然你以为我为什么要自己总结&#xff01;&#xff01;&#xff01; IDEA 修改插件安装位置 前言步骤 前言 IDEA 默认的配置文件均安装在C盘&#xff0c;使用时间长会生成很多文件&#xff0c;这些文件会占用挤兑C盘空间&…

2023年中国合同能源管理行业研究报告

第一章 行业概况 1.1 定义及分类 合同能源管理 (Energy Performance Contracting, EPC) 是当前能源行业中一个重要的概念&#xff0c;它构建了一个桥梁&#xff0c;将节能服务公司 (Energy Management Company, EMCo) 与用能单位紧密联系在一起。通过特定的契约形式&#xff…

解决ERROR: No query specified的错误以及\G 和 \g 的区别

文章目录 1. 复现错误2. 分析错误3. 解决问题4. \G和\g的区别 1. 复现错误 今天使用powershell连接数据库后&#xff0c;执行如下SQL语句&#xff1a; mysql> select * from student where id 39 \G;虽然成功查询除了数据&#xff0c;但报出如下错误的信息&#xff1a; my…

openGauss学习笔记-98 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置客户端接入认证

文章目录 openGauss学习笔记-98 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置客户端接入认证98.1 背景信息98.2 操作步骤98.3 异常处理98.4 示例 openGauss学习笔记-98 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置客户端接入认证 98.1 背景信息 …

用 docker 创建 jmeter 容器, 实现性能测试,该如何下手?

用 docker 创建 jmeter 容器, 实现性能测试 我们都知道&#xff0c;jmeter可以做接口测试&#xff0c;也可以用于性能测试&#xff0c;现在企业中性能测试也大多使用jmeter。docker是最近这些年流行起来的容器部署工具&#xff0c;可以创建一个容器&#xff0c;然后把项目放到…

开源数据库MySQL 8.0 OCP认证精讲视频、环境和题库 之三 选项、变量

选项文件&#xff1a;默认/etc/my.cnf 可以通过以下选项&#xff0c;指定选项文件&#xff1a; -defaults-file&#xff1a;指定选项文件 例如:mysql--defaults-file/etc/my.cnf -no-defaults&#xff1a;不读任何选项文件&#xff0c;所有选项需要在命令行中指定 -defaults-ex…

在Vue+Ts+Vite项目中如何配置别名指向不同的目录并引用

在VueTsVite项目中如何配置别名指向不同的目录并引用 vite.config.ts配置如下&#xff1a;tsconfig.json中需要配置baseUrl和paths,如下所示&#xff1a;项目中直接引入案例&#xff1a; vite.config.ts配置如下&#xff1a; import { defineConfig, AliasOptions } from vite…

FPGA project : flash_secter_erase

flash的指定扇区擦除实验。 先发写指令&#xff0c;再进入写锁存周期等待500ns&#xff0c;进入写扇区擦除指令&#xff0c;然后写扇区地址&#xff0c;页地址&#xff0c;字节地址。即可完成扇区擦除。 模块框图&#xff1a; 时序图&#xff1a; 代码&#xff1a; module…

后厂村路灯:苹果签名是什么? 苹果签名有什么作用?苹果签名能做什么?原理是什么?有哪些方式?

苹果签名是苹果公司为了保证iOS设备安全性而推出的一种机制。 当ios APP辛苦开发完成后&#xff0c;通常去苹果商店进行上架&#xff0c;用户才能下载&#xff0c;不能上架或者上架前测试的话&#xff0c;进行签名使之可以下载。简单说&#xff0c;苹果的ios签名是一种允许苹果…

香港服务器选纯国际线路上网稳定吗?

​  关于香港服务器的线路&#xff0c;我们平时接触较多的分三大类&#xff0c;即纯国际线路、回国专线和香港本地线路。三者价格上存有差距&#xff0c;原因体现在线路和网络质量上&#xff0c;当然这些会关系到服务器的速度和稳定性。譬如&#xff0c;有些用户在选择了纯国…

部署zabbix代理服务器、部署zabbix高可用集群

目录 部署zabbix代理服务器 1、环境设置 2、设置 zabbix 的下载源&#xff0c;安装 zabbix-proxy 3、配置Mariadb yum源&#xff0c;并下载marisdb数据库 4.、启动数据库&#xff0c;并初始化数据库 5、登录数据库&#xff0c;创建数据库并指定字符集&#xff0c;并进行…

怎样制作一个展会场馆预约小程序

随着互联网的发展&#xff0c;展会行业也逐渐向数字化转型。展会场馆预约小程序作为展会线下向线上的延伸&#xff0c;能够让参展商和观众随时随地进行预约&#xff0c;大大提升了客户的体验。那么&#xff0c;如何制作一个展会场馆预约小程序呢&#xff1f;下面就以乔拓云平台…

迷你Ceph集群搭建(超低配设备)

我的博客原文链接&#xff1a;https://blog.gcc.ac.cn/post/2023/%E8%BF%B7%E4%BD%A0ceph%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA/ 环境 机器列表&#xff1a; IP角色说明10.0.0.15osdARMv7&#xff0c;512M内存&#xff0c;32G存储&#xff0c;百兆网口10.0.0.16clientARM64…

分享关于msvcp110dll丢失的5种不同解决方法

今天我要和大家分享的主题是&#xff1a;msvcp110dll丢失的5种不同解决方法。我们都知道&#xff0c;在电脑使用过程中&#xff0c;经常会遇到一些棘手的问题&#xff0c;比如msvcp110.dll丢失。那么&#xff0c;这个文件丢失到底是什么意思呢&#xff1f;接下来&#xff0c;我…

Python数据挖掘项目实战——自动售货机销售数据分析

摘要&#xff1a;本案例将主要结合自动售货机的实际情况&#xff0c;对销售的历史数据进行处理&#xff0c;利用pyecharts库、Matplotlib库进行可视化分析&#xff0c;并对未来4周商品的销售额进行预测&#xff0c;从而为企业制定相应的自动售货机市场需求分析及销售建议提供参…

国内常用源开发环境换源(flutter换源,python换源,Linux换源,npm换源)

flutter换源 使用环境变量:PUB_HOSTED_URL FLUTTER_STORAGE_BASE_URL&#xff0c; upgrade出问题时可能会提示设置FLUTTER_GIT_URL变量。 flutter中国 PUB_HOSTED_URLhttps://pub.flutter-io.cn FLUTTER_STORAGE_BASE_URLhttps://storage.flutter-io.cn FLUTTER_GIT_URLhtt…

高并发下的服务容错

在微服务架构中&#xff0c;我们将业务拆分成一个个的服务&#xff0c;服务与服务之间可以相互调用&#xff0c;但是由于网络 原因或者自身的原因&#xff0c;服务并不能保证服务的100%可用&#xff0c;如果单个服务出现问题&#xff0c;调用这个服务就会 出现网络延迟&#xf…

C# OCR服务测试程序

效果 项目 代码 using NLog; using RestSharp; using RestSharp.Contrib; using System; using System.Drawing; using System.IO; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms;namespace OCRSe…

解决react样式组合时css module动态样式失效的问题

现象&#xff1a; <button disabled{invalid} className{ "btn btn-primary btn-lg" invalid ? styles.btnDisabled : "" } > 注册 </button> 上面采用字符串拼接的方式&#xff0c;组合class&#xff0c;但是css module的动态样式style…