欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS分別利用分頁加載和懶加載預(yù)覽PDF

 更新時(shí)間:2024年01月15日 08:41:03   作者:可樂雞翅kele  
在開發(fā)過程中,我們經(jīng)常會(huì)遇到預(yù)覽PDF的需求,這篇文章主要為大家詳細(xì)介紹了JS如何利用分頁加載和懶加載預(yù)覽PDF,希望對(duì)大家有所幫助

前言

在開發(fā)過程中,我們經(jīng)常會(huì)遇到預(yù)覽PDF的需求。這個(gè)時(shí)候我們一般是會(huì)有一個(gè)PDF的地址,然后通過一些手段將PDF文件預(yù)覽在頁面上。比如說以下的方式或者第三方庫:

  • embed標(biāo)簽
  • iframe標(biāo)簽
  • pdf.js
  • react-pdf

但是當(dāng)PDF比較大的時(shí)候,加載速度緩慢以及占用內(nèi)存過大會(huì)是一個(gè)比較嚴(yán)重的問題。PDF體積較大時(shí),網(wǎng)絡(luò)傳輸?shù)臅r(shí)間必然會(huì)較長,這樣用戶的體驗(yàn)會(huì)比較差;當(dāng)打開較大的PDF時(shí),會(huì)占用較多的內(nèi)存,可能會(huì)導(dǎo)致標(biāo)簽頁/瀏覽器卡死或者崩潰。

常規(guī)預(yù)覽方式

先來看看常規(guī)的PDF預(yù)覽方式,這里簡(jiǎn)單提兩種預(yù)覽方式,都是十分簡(jiǎn)單的,代碼如下:

import { PDF_URL } from "./constant"

const Normal = () => {
  return (
    <div>
      <embed src={PDF_URL} type="application/pdf" width="100%" height="600px"></embed>
      <iframe width={'100%'} height={600} src={PDF_URL} />
    </div>
  )
}

export default Normal

一種使用embed標(biāo)簽去預(yù)覽,另一種是用iframe標(biāo)簽去預(yù)覽,這里都是借助于瀏覽器自帶的PDF渲染引擎去把PDF預(yù)覽出來。也是平時(shí)我們用的比較多的一種方式。

但我這是一個(gè)15M左右,100頁+PDF,在我4M帶寬的服務(wù)器下,傳輸時(shí)間需要大概30秒左右,時(shí)間還是相當(dāng)長的。

所以下面要介紹的是PDF的分頁加載和渲染。旨在解決大PDF網(wǎng)絡(luò)傳輸過慢以及占用內(nèi)存過大的問題。

按圖片分割

既然一次性加載整個(gè)PDF文件太過緩慢,那我們可以考慮把它拆分成一個(gè)個(gè)更細(xì)的單元去進(jìn)行預(yù)覽。這里我可以先把一整個(gè)PDF文件按頁碼轉(zhuǎn)成一張張圖片,1頁PDF對(duì)應(yīng)1張圖片。那么經(jīng)過這樣的預(yù)處理之后,我們就很容易能夠做到按需加載、或者分片加載,而不是一次性加載一整個(gè)PDF文件。這樣用戶的首屏等待時(shí)間會(huì)大大降低,可以提升用戶體驗(yàn)。

PDF轉(zhuǎn)圖片

這里需要先預(yù)處理一下數(shù)據(jù),將PDF文件轉(zhuǎn)換為圖片。我這里使用的是 pdf-image 這個(gè)庫,具體的安裝教程可以點(diǎn)擊鏈接查看。

安裝完之后可以用node寫一個(gè)這樣的預(yù)處理腳本,將所需要處理的PDF文件的每一頁都轉(zhuǎn)換成一張圖片:

const PDFImage = require("pdf-image").PDFImage;
const path = require("path");
const fs = require("fs");
const FILE_PATH = path.join(__dirname, "../public/files/test.pdf");
const outputDirectory = path.join(__dirname, "../public/files/tmp");

const pdfImage = new PDFImage(FILE_PATH, {
  convertOptions: {
    "-density": 250, // 提高分辨率,根據(jù)需要調(diào)整
    "-quality": 100,
    "-trim": null,
  },
  outputDirectory,
});

const run = async () => {
  const info = await pdfImage.getInfo();
  for (let i = 0; i < Number(info.Pages); i++) {
    try {
      await pdfImage.convertPage(i);
    } catch (error) {
      console.log("error", error);
    }
  }
};
run();

拿到了每一頁的圖片之后,我們就可以拿這些圖片去做預(yù)覽功能了。這里有100多張圖片,渲染的時(shí)候不需要一次性將它們渲染出來,可以做一下懶加載。這里我是使用IntersectionObserver去實(shí)現(xiàn)了圖片的懶加載。先實(shí)現(xiàn)一個(gè)懶加載的hook如下:

  • 這里可以先給每一個(gè)元素一個(gè)默認(rèn)的高度,然后利用IntersectionObserver監(jiān)聽元素是否出現(xiàn)在視口范圍內(nèi)
  • 如果出現(xiàn)在視口返回內(nèi),就加載這張圖片
import { useEffect,useRef,useCallback } from "react";

const useObserve = ({ rootRef, itemSelector }) => {
  const observer = useRef(null);

  const handlerObserve = entries => {
    entries.forEach(({ isIntersecting, target }) => {
      if (isIntersecting) {
        const targetImg = target.children[0];
        targetImg.src = targetImg.dataset.src;
        // 修改過src屬性之后,即可移除data-src屬性并且取消監(jiān)視
        targetImg.removeAttribute("data-src");
        observer.current.unobserve(target);
      }
    });
  };
  const addObserve = () => {
    const list = document.querySelectorAll(itemSelector);
    list.forEach(item => {
      observer.current.observe(item);
    });
  };

  const initObserver = useCallback(() => {
    observer.current = new IntersectionObserver(handlerObserve, {
      root: rootRef.current,
      rootMargin: "0px 0px 200px 0px", // 監(jiān)視區(qū)向下拓展200px
    });
    addObserve();
  }, []);
  useEffect(() => {
    initObserver();
    return () => {
      observer.current.disconnect();
    };
  }, []);
};

export default useObserve;

然后實(shí)現(xiàn)預(yù)覽組件如下:

import { Carousel } from "antd";
import styles from "./index.module.less";
import { LeftOutlined, RightOutlined } from "@ant-design/icons";
import { useState, useEffect, useRef, useMemo, useCallback } from "react";
import { API_PREFIX } from "../../../../env";
import useObserve from "./useObserve";
const defaultImage = "";

const Preview = () => {
  const [list, setList] = useState(() => {
    return new Array(105).fill(0).map((item, index) => {
      return {
        page: index,
        originSrc: `${API_PREFIX}/files/tmp/test-${index}.png`,
      };
    });
  });
  const contentRef = useRef(null);
  useObserve({
    rootRef: contentRef,
    itemSelector: ".image-item-observe",
  });
  return (
    <div className={styles.preview}>
      <div ref={contentRef} className={styles.content}>
        {list.map((item, index) => {
          return (
            <div class={`${styles["image-item-wrapper"]} image-item-observe`}>
              {/* 設(shè)置一個(gè)默認(rèn)的缺省圖,避免在加載過程中出現(xiàn)白屏的現(xiàn)象 */}
              <img
                class={styles["image-item"]}
                src={defaultImage}
                data-src={item.originSrc}
                key={index}
                width="100%"
              />
            </div>
          );
        })}
      </div>
    </div>
  );
};

export default Preview;

這樣就實(shí)現(xiàn)了按需加載預(yù)覽PDF的功能

按頁分割

上面介紹的是把PDF轉(zhuǎn)成圖片之后,利用加載多圖的方式來做PDF的懶加載。下面這種方式是,把一個(gè)大的PDF切分成若干個(gè)小的PDF,再使用常規(guī)的預(yù)覽方式去預(yù)覽。首先還是寫一個(gè)腳本來預(yù)處理數(shù)據(jù),切分PDF文件。

const fs = require("fs");
const { PDFDocument } = require("pdf-lib");
const path = require("path");
async function splitPDF(inputPath, outputPrefix) {
  const pdfBytes = await fs.promises.readFile(inputPath);
  const pdfDoc = await PDFDocument.load(pdfBytes);

  const totalPages = pdfDoc.getPageCount();
  const pagesPerChunk = 10;

  for (let startPage = 1; startPage <= totalPages; startPage += pagesPerChunk) {
    const endPage = Math.min(startPage + pagesPerChunk - 1, totalPages);

    const newPdfDoc = await PDFDocument.create();

    for (let pageNum = startPage; pageNum <= endPage; pageNum++) {
      const [copiedPage] = await newPdfDoc.copyPages(pdfDoc, [pageNum - 1]);
      newPdfDoc.addPage(copiedPage);
    }

    const outputPath = `${outputPrefix}/${Math.floor(endPage / 10)}.pdf`;
    const newPdfBytes = await newPdfDoc.save();
    await fs.promises.writeFile(outputPath, newPdfBytes);

    console.log(`PDF successfully split from page ${startPage} to ${endPage}.`);
  }
}

const FILE_PATH = path.join(__dirname, "../public/files/test.pdf");
const outputDirectory = path.join(__dirname, "../public/files/tmp-file");
splitPDF(FILE_PATH, outputDirectory);

切分后的結(jié)果如下:

然后預(yù)覽的時(shí)候就十分簡(jiǎn)單,我是直接用iframe去一份份的預(yù)覽,點(diǎn)擊加載更多再去加載下一個(gè)片段。一個(gè)簡(jiǎn)單的預(yù)覽組件如下,一個(gè)個(gè)地加載我們分割的PDF文件:

import React, { useState, useEffect, useRef } from "react";
import { API_PREFIX } from "../../../../env";
const pdfs = new Array(11)
  .fill(0)
  .map((_, index) => `${API_PREFIX}/files/tmp-file/${index + 1}.pdf`);

const Split = () => {
  const [list, setList] = useState([pdfs[0]]);
  return (
    <div>
      {list.map((url, index) => {
        return (
          <div>
            <iframe
              id={`iframe-${index}`}
              key={index}
              width={"100%"}
              height={600}
              src={url}
            />
          </div>
        );
      })}
      <button
        disabled={list.length === pdfs.length}
        onClick={() => {
          const arr = [...list];
          arr.push(pdfs[arr.length]);
          setList(arr)
        }}
      >
        加載更多
      </button>
    </div>
  );
};

export default Split;

以上就是JS分別利用分頁加載和懶加載預(yù)覽PDF的詳細(xì)內(nèi)容,更多關(guān)于JS預(yù)覽PDF的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • BootStrap表單驗(yàn)證 FormValidation 調(diào)整反饋圖標(biāo)位置的實(shí)例代碼

    BootStrap表單驗(yàn)證 FormValidation 調(diào)整反饋圖標(biāo)位置的實(shí)例代碼

    這篇文章主要介紹了BootStrap表單驗(yàn)證 FormValidation 調(diào)整反饋圖標(biāo)位置的實(shí)例代碼,需要的朋友可以參考下
    2017-05-05
  • javascript 詞法作用域和閉包分析說明

    javascript 詞法作用域和閉包分析說明

    以下上是我在學(xué)習(xí)和使用了JS一段時(shí)間后,為了更深入的了解它, 也為了更好的把握對(duì)它的應(yīng)用, 從而在對(duì)閉包的學(xué)習(xí)過程中,自己對(duì)于詞法作用域的一些理解和總結(jié)
    2010-08-08
  • webpack@v4升級(jí)踩坑(小結(jié))

    webpack@v4升級(jí)踩坑(小結(jié))

    這篇文章主要介紹了webpack@v4升級(jí)踩坑(小結(jié)),之前就嘗試了升級(jí),由于部分插件的原因,未能成功,今天就來試一下在我的項(xiàng)目中升級(jí)會(huì)遇到哪些坑。感興趣的小伙伴們可以參考一下
    2018-10-10
  • javascript實(shí)現(xiàn)搜索篩選功能實(shí)例代碼

    javascript實(shí)現(xiàn)搜索篩選功能實(shí)例代碼

    這篇文章主要給大家介紹了關(guān)于javascript實(shí)現(xiàn)搜索篩選功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • JavaScript中定義類的方式詳解

    JavaScript中定義類的方式詳解

    這篇文章主要介紹了JavaScript中定義類的方式,結(jié)合實(shí)例形式分析了JavaScript實(shí)現(xiàn)面向?qū)ο箢惖亩x及使用相關(guān)技巧,并附帶了四種JavaScript類的定義方式,需要的朋友可以參考下
    2016-01-01
  • 純JS實(shí)現(xiàn)輪播圖

    純JS實(shí)現(xiàn)輪播圖

    這幾天一直在看js動(dòng)畫,今天又get到了一個(gè)輪播圖,使用純js實(shí)現(xiàn)的,但是外觀樣式不是很好看,如果大家有需要可以美化下,具體實(shí)現(xiàn)代碼還是很完整的,大家可以參考下
    2017-02-02
  • 使用濾鏡設(shè)置透明導(dǎo)致 IE 6/7/8/9 解析異常的解決方法

    使用濾鏡設(shè)置透明導(dǎo)致 IE 6/7/8/9 解析異常的解決方法

    使用濾鏡設(shè)置透明導(dǎo)致 IE 6/7/8/9 解析異常的解決方法,需要的朋友可以參考下。
    2011-04-04
  • JavaScript Html實(shí)現(xiàn)移動(dòng)端紅包雨功能頁面

    JavaScript Html實(shí)現(xiàn)移動(dòng)端紅包雨功能頁面

    這篇文章主要為大家詳細(xì)介紹了JavaScript Html實(shí)現(xiàn)移動(dòng)端紅包雨功能頁面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • 使用iframe實(shí)現(xiàn)pdf文件預(yù)覽功能

    使用iframe實(shí)現(xiàn)pdf文件預(yù)覽功能

    這篇文章主要為大家詳細(xì)介紹了如何使用iframe實(shí)現(xiàn)pdf文件預(yù)覽功能,以及iframe預(yù)覽報(bào)錯(cuò)問題和iframe未能加載PDF文檔,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-09-09
  • uniapp解決token值無法使用的問題

    uniapp解決token值無法使用的問題

    這篇文章主要給大家介紹了如何使用uniapp解決token值無法使用的問題,文中給大家詳細(xì)介紹了產(chǎn)生原因和解決方案,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-10-10

最新評(píng)論