You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在react-pdf库中避免同一用户会话重复发起PDF文件API请求

React会话内避免重复请求PDF的实现方案

下面是几种实用的实现方式,根据你的场景选择:

1. 内存级缓存(组件/全局共享)

适合单组件内或全局会话内的临时缓存,页面刷新后缓存会清空。可以用useRef(不触发重渲染)或者全局对象来存储:

import { useRef, useState } from 'react';

const PdfViewer = ({ pdfId }) => {
  const pdfCache = useRef({}); // 用useRef存缓存,避免重渲染
  const [pdfData, setPdfData] = useState(null);
  const [loading, setLoading] = useState(false);

  const fetchPdf = async () => {
    // 先检查缓存是否存在
    if (pdfCache.current[pdfId]) {
      setPdfData(pdfCache.current[pdfId]);
      return;
    }

    setLoading(true);
    try {
      const res = await fetch(`/api/pdf/${pdfId}`);
      const blob = await res.blob();
      // 存入缓存
      pdfCache.current[pdfId] = blob;
      setPdfData(blob);
    } catch (err) {
      console.error('PDF请求失败:', err);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <button onClick={fetchPdf} disabled={loading}>加载PDF</button>
      {pdfData && <iframe src={URL.createObjectURL(pdfData)} width="100%" height="600px" />}
    </div>
  );
};

如果需要全局跨组件共享缓存,可以在组件外定义一个全局缓存对象,或者用React Context传递缓存实例。

2. SessionStorage缓存(跨页面/刷新保留)

如果需要在同一会话的不同页面或刷新后依然保留缓存,可以用sessionStorage,它的存储周期和用户会话一致:

const fetchPdf = async (pdfId) => {
  const cacheKey = `pdf_${pdfId}`;
  // 先从sessionStorage读取缓存
  const cachedBase64 = sessionStorage.getItem(cacheKey);
  if (cachedBase64) {
    // 将base64转回Blob对象
    const byteChars = atob(cachedBase64);
    const byteNumbers = Array.from(byteChars, char => char.charCodeAt(0));
    const byteArray = new Uint8Array(byteNumbers);
    return new Blob([byteArray], { type: 'application/pdf' });
  }

  // 无缓存时请求API
  const res = await fetch(`/api/pdf/${pdfId}`);
  const blob = await res.blob();
  
  // 将Blob转成base64存入sessionStorage
  const reader = new FileReader();
  reader.onloadend = () => {
    const base64Data = reader.result.split(',')[1];
    sessionStorage.setItem(cacheKey, base64Data);
  };
  reader.readAsDataURL(blob);
  
  return blob;
};

注意:sessionStorage有大小限制(通常5MB左右),大体积PDF不适合用这个方案。

3. 封装自定义Hook复用缓存逻辑

把缓存逻辑抽成自定义Hook,方便多个组件复用:

import { useRef, useCallback, useState } from 'react';

const usePdfCache = () => {
  const cache = useRef({});

  const getPdf = useCallback(async (pdfId) => {
    // 优先取缓存
    if (cache.current[pdfId]) {
      return cache.current[pdfId];
    }

    // 请求并缓存
    const res = await fetch(`/api/pdf/${pdfId}`);
    const blob = await res.blob();
    cache.current[pdfId] = blob;
    return blob;
  }, []);

  return { getPdf };
};

// 使用示例
const PdfComponent = ({ pdfId }) => {
  const { getPdf } = usePdfCache();
  const [pdfBlob, setPdfBlob] = useState(null);

  const handleLoad = async () => {
    const blob = await getPdf(pdfId);
    setPdfBlob(blob);
  };

  return (
    <>
      <button onClick={handleLoad}>加载PDF</button>
      {pdfBlob && <iframe src={URL.createObjectURL(pdfBlob)} width="100%" height="500px" />}
    </>
  );
};

关键注意点

  • 缓存键唯一性:用PDF的唯一ID(比如后端返回的文件ID)作为缓存键,避免不同PDF互相覆盖。
  • 缓存失效:如果PDF内容可能在会话内更新,可以给缓存条目添加时间戳,或者提供手动清空缓存的方法。
  • 大文件处理:大体积PDF优先用内存缓存,避免sessionStorage存储溢出。
  • 错误处理:保留请求错误捕获逻辑,避免请求失败后返回无效缓存。

内容的提问来源于stack exchange,提问作者Vamsi Krishna Lakshmipuram

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 18:01:13