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

NextJS书签模块显示本地存储favicon遇安全错误求助

问题描述

在NextJS应用中实现书签功能:用户输入网址后,系统获取目标网站favicon并存入localStorage,页面加载时从localStorage读取并显示。目前favicon已成功存入localStorage,但显示失败,控制台报两类错误:

  • Security Error: Content at http://localhost:3000/ may not load data from blob:http://localhost:3000/fa614f8e-0ec6-48ef-ad52-0cce4f514d0e.
  • GET blob:http://localhost:3000/4df97d52-2946-4389-97d5-7c734b706224 net::ERR_FILE_NOT_FOUND

已尝试调整图片创建方式、状态更新逻辑及Content Security Policy(CSP)配置,均未解决问题。相关代码如下:

相关代码

SingleBookmark.tsx

const [faviconSource, setFaviconSource] = useState(null);

useEffect(() => {
  if (!localStorage.getItem(name)) {
    fetchFaviconToStorage();
  } else {
    setFaviconSource(JSON.parse(localStorage.getItem(name)));
  }
}, []);

const fetchFaviconToStorage = () => {
  const imgurl =
    `https://thingproxy.freeboard.io/fetch/http://${name}/favicon.ico`;
  fetch(imgurl)
    .then((response) => response.blob())
    .then((myBlob) => {
      const imageObjectURL = URL.createObjectURL(myBlob);
      localStorage.setItem(name, JSON.stringify(imageObjectURL));
      setFaviconSource(imageObjectURL);
    });
};

return (
<>
  {faviconSource &&
     <Image
         loader={(props) => imgixLoader(props, { fit: "crop", ar: "1:1" })}
         src={faviconSource}   
     />
  }
</>
);

next.config.js

const ContentSecurityPolicy = `
img-src 'self' data: blob:;
`;
const securityHeaders = [
  {
    key: "Content-Security-Policy",
    value: ContentSecurityPolicy.replace(/\s{2,}/g, " ").trim(),
  },
];

module.exports = {
  async headers() {
    return [
      {
        // Apply these headers to all routes in your application.
        source: "/:path*",
        headers: securityHeaders,
      },
    ];
  },
};

问题原因

  1. Blob URL的临时性:URL.createObjectURL()生成的Blob URL仅在当前页面会话有效,页面刷新或关闭后会失效。将这类URL存入localStorage,下次加载时已无法访问,导致ERR_FILE_NOT_FOUND错误。
  2. Next.js Image组件的Loader冲突:使用imgixLoader加载Blob URL或base64资源时,Loader会尝试将资源路径转换为imgix格式,破坏原有资源地址结构,引发安全错误。

解决方案

1. 将Blob转换为Base64存储

把favicon的Blob数据转为Base64字符串存入localStorage,Base64是持久化的文本格式,可跨会话使用。

2. 调整Image组件配置

移除或修改imgixLoader,直接使用Next.js默认的资源处理逻辑加载Base64图片。

修改后的代码

SingleBookmark.tsx

const [faviconSource, setFaviconSource] = useState<string | null>(null);

useEffect(() => {
  const storedFavicon = localStorage.getItem(name);
  if (!storedFavicon) {
    fetchFaviconToStorage();
  } else {
    setFaviconSource(storedFavicon);
  }
}, [name]);

const fetchFaviconToStorage = async () => {
  try {
    const imgurl = `https://thingproxy.freeboard.io/fetch/http://${name}/favicon.ico`;
    const response = await fetch(imgurl);
    const blob = await response.blob();
    
    // 将Blob转为Base64
    const reader = new FileReader();
    reader.onloadend = () => {
      const base64Data = reader.result as string;
      localStorage.setItem(name, base64Data);
      setFaviconSource(base64Data);
    };
    reader.readAsDataURL(blob);
  } catch (error) {
    console.error("获取favicon失败:", error);
  }
};

return (
  <>
    {faviconSource && (
      <Image
        src={faviconSource}
        alt={`${name} favicon`}
        width={32}
        height={32} // 必须指定宽高,Next.js Image组件要求
      />
    )}
  </>
);

next.config.js(无需修改,保持原有CSP配置即可)

const ContentSecurityPolicy = `
img-src 'self' data: blob:;
`;
const securityHeaders = [
  {
    key: "Content-Security-Policy",
    value: ContentSecurityPolicy.replace(/\s{2,}/g, " ").trim(),
  },
];

module.exports = {
  async headers() {
    return [
      {
        source: "/:path*",
        headers: securityHeaders,
      },
    ];
  },
};

关键说明

  • Base64格式可直接作为Image组件的src值,无需额外Loader处理,避免了路径转换冲突。
  • 必须为Next.js Image组件指定width和height属性,或使用layout="responsive"等布局属性,否则会报错。
  • 异常处理可避免因目标网站无favicon导致的页面崩溃。

内容的提问来源于stack exchange,提问作者Fivis420

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 00:24:15