React 实现PDF预览(数据源使用文件流而不是url)

一 前提

应公司要求,需要进行上传文件(pdf)的预览功能,网上大部分都是使用url作为预览数据源,但是现在后端那边只返回了pdf文件流,所以本文主要是用文件流来预览pdf。

二 首先需要获取pdf文件流,并转化为base64格式的数据。

1. 封装axios方法,用于获取pdf文件流

javascript 复制代码
const apiClient = axios.create({
  baseURL: '/', // 1.测试环境 /aidss-api
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  }
});

// 封装的axiosget方法,获取pdf文件流 
const axiosGetFileBlob = async (url: string) => {
  try {
    const configs: any = {
      method: 'get',
      url: `${url}`,
      responseType: 'blob'
    };
    const res = await apiClient.get(url, configs);

    return res.data;
  } catch (error) {
     // 抛出异常信息
  }
  return null as any;
};

2.调用封装的方法,获取文件流并转为base64格式的数据。

javascript 复制代码
 // 将文件流转为base64格式
const getBase64 = (file: any) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result);
    reader.onerror = error => reject(error);
  });
};  

const getData = async () => {
    const res = await axiosGetFileBlob("这里是获取pdf的url");
    if (res) {
       // 这里获取到了文件流,首先需要转化为base64格式的数据
      const baseData = await getBase64(res);
      // 转化为base64的数据有一个文件类型的前缀,需要去掉。
      const resourceData = baseData.split(',')[1];
      // 这里需要拼接一个前缀,表示这个是一个pdf文件
      const finalData= "data:application/pdf;base64," + resourceData ;

      consloe.log("finalData就是获取到的pdf文件流");
    }
  }

三 进行pdf预览功能

根据第二步,可以拿到后端获取后并转化为base64格式的文件流数据。

pdf预览有下列几种方式,主要分为系统自带预览(iframe,object,embed )以及插件预览(react-pdf-js)。大家可以自行选用合适。

1. 使用系统自带的方式预览(iframe,object,embed)。

使用方式很简单,直接将第二步的finalData放到各自的数据源属性下。代码如下:

javascript 复制代码
// 使用iframe
<iframe src={finalData+ "#toolbar=0"} height={800}>您的电脑不支持iframe</iframe>

// 使用embed
<embed src={finalData+ "#toolbar=0"} height={800}/>

// 使用 object
<object data={finalData+ "#toolbar=0"} height={800}></object>

效果如下:

备注:

预览的pdf大小自由设置标签的属性即可。

#toolbar=0 代码的意思是隐藏顶部工具栏(下载、打印等),如果不加,则效果如下:

2.使用插件的方式预览(react-pdf-js)。

(1) 首先需要安装:

yarn add @mikecousins/react-pdf 或者 npm install @mikecousins/react-pdf

(2)实现代码如下:

javascript 复制代码
import React, {useEffect, useState} from "react";
import PDF from 'react-pdf-js';
// getFileBlob 就是上面提到过的:从后端拿取数据流
import {getFileBlob} from "@/services/common";
// getBase64 就是将数据转为base64格式
import {getBase64} from "@/utils/common";

export default () => {
  const [totalPage, setTotalPages] = useState(1);
  const [currentPage, setCurrentPage] = useState(1);
  // 保存后端获取到后并转化为base64的数据流
  const [pdfBlob, setPdfBlob] = useState<any>(null);

  const getData = async () => {
    const res = await getFileBlob("20240902095451482799");
    debugger
    if (res) {
      const baseData = await getBase64(res);
      const resourceData = baseData.split(',')[1];
      if (resourceData) {
        // 有数据再进行拼接,否则无效
        const finalData = "data:application/pdf;base64," + resourceData;
        setPdfBlob(finalData);
      }
    }
  }
  useEffect(() => {
    getData();
  });

  const onDocumentLoadSuccess = (totalPage: any) => {
    setTotalPages(totalPage);
    setCurrentPage(1);
  }

  // 一次展示所有界面
  const showAllPages = () => {
    const page = [];
    for (let i = 2; i <= totalPage; i++)
      page.push(<PDF page={i} key={`page-${i}`} file={finalPdfBlob} scale={1.5}/>);
    return page;
  }

  return (
    <div>
      {/* 一定要注意(必须要先进行pdfBlob的判定,pdfBlob 为空的话,不能继续下去。否则系统会报错。)*/}
      {
        pdfBlob && <div>
          <PDF
            scale={1.5}
            file={pdfBlob}
            onDocumentComplete={onDocumentLoadSuccess}
            page={currentPage}
          />
          {/* 一次性展示全部页面:代码如下 */}
          {totalPage > 1 && showAllPages()}
          {/*  这里也可以添加分页组件 ,进行分页展示,调用 setCurrentPage 即可。 */}
        </div>
      }

    </div>
  );
}

四 应用(在浏览器新打开的窗口中预览)

我这里的预览使用的是iframe。将上面拿到的base64串,放到iframe中作为数据源。再结合window方法即可。代码如下:

javascript 复制代码
<Button onClick={()=>getPdfContent("123456")}>预览<Button>  

 // 点击方法
const getPdfContent = async (id: string) => {
    try {
      // 从后端获取pdf数据流
      const pdfBlob = await getFileBlob(id);
      if (pdfBlob) {
        // 将pdf数据流转为base64字符串
        const pdfBase64 = await getBase64(pdfBlob);
        if (pdfBase64) {
          //获取的数据有前缀,不满足下载的pdf格式。需要去掉后,单独拼接pdf的格式
          const pdfData = pdfBase64.toString().split(',')[1]; 
          const iframeSrc = `data:application/pdf;base64,${pdfData}`;
          // -----------------关键位置------------
          // 创建window.open方法,并将iframe结合数据源写入。
          const pdfWindow = window.open("", id);
          pdfWindow?.document.write(`<iframe width='100%' height='100%' src=${iframeSrc}></iframe>`);
          pdfWindow?.document.close();
        }
      }
    } catch (e) {
      console.log("系统错误!");
    }

  };
相关推荐
aiguangyuan16 分钟前
浅谈 React Hooks
react·前端开发
whatever who cares2 天前
React hook之userReducer
react.js·react
aiguangyuan2 天前
React Hooks 基础指南
react·前端开发
aiguangyuan3 天前
React 项目初始化与搭建指南
react·前端开发
aiguangyuan3 天前
React 组件异常捕获机制详解
react·前端开发
aiguangyuan3 天前
深入理解 JSX:React 的核心语法
react·前端开发
aiguangyuan4 天前
React 基础语法
react·前端开发
aiguangyuan5 天前
React 核心概念与生态系统
react·前端开发
aiguangyuan5 天前
React 18 生命周期详解与并发模式下的变化
react·前端开发
linweidong13 天前
汇量科技前端面试题及参考答案
webpack·vue3·react·前端面试·hooks·懒加载·flex布局