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

Next.js 13 App Router中opengraph-image.tsx动态导图报错排查

动态URL触发fetch失败的原因及解决方法

问题现象

动态拼接ID生成URL传入fetch时抛出Error [TypeError]: fetch失败错误,替换为静态玩家ID的URL时,图片可正常加载。

场景说明

在玩家详情页的OpenGraph图片生成逻辑中,需要根据玩家ID动态从public文件夹获取对应图片。使用${id}动态拼接路径时fetch失败,换成固定ID的静态路径则能正常加载。

代码示例

路径:src/app/players/[id]/opengraph-image.tsx

import { ImageResponse } from 'next/server';

export const size = { width: 1200, height: 680 };
export const contentType = 'image/png';
export const runtime = 'edge';
export default async function og({ params }) {
  const {id} = params;
  const image = await fetch(new URL(`../../../../public/assets/images/players/png/${id}.png`, import.meta.url)).then(
    (res) => res.arrayBuffer()
  );
  const interBold = fetch(
    new URL('../../../../public/assets/fonts/Inter-Bold.ttf', import.meta.url)
  ).then((res) => res.arrayBuffer())
  
  return new ImageResponse(
    <div
      style={{
        height: '100%',
        width: '100%',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: '#fff',
        fontSize: 52,
        fontWeight: 600,
        padding: '15px 20px',
        color: '#1e2124',
        margin: '0 auto',
      }}
    >
      {/* @ts-ignore */}
      <img src={image} width="425" height="430" alt={id} style={{ margin: '0 0 15px 0' }}/>
    </div>
  , {
    ...size,
    fonts: [
      {
        name: 'Inter',
        data: await interBold,
        style: 'normal',
        weight: 600,
      },
    ]
  }
  );
}

错误信息

Error [TypeError]: fetch失败

原因分析

  1. 静态构建限制:Next.js在构建阶段会解析静态URL,将import.meta.url结合相对路径转换为正确的资源路径。但动态拼接的路径无法被构建工具提前识别,运行时生成的路径无法映射到public文件夹的实际资源位置。
  2. Edge Runtime特性:Edge环境中本地文件的访问逻辑与Node.js不同,直接通过相对路径动态拼接URL对象,无法正确解析到public目录下的静态资源,导致fetch请求的目标路径无效。
  3. 资源路径映射规则:public文件夹下的资源在部署后会被映射到域名根目录,使用相对路径(如../../../../public/...)动态拼接时,实际请求的路径不符合Next.js的资源映射规则。

解决方法

方法1:使用绝对URL访问public资源

通过环境变量配置应用的基础URL,动态拼接成绝对URL进行fetch:

import { ImageResponse } from 'next/server';

export const size = { width: 1200, height: 680 };
export const contentType = 'image/png';
export const runtime = 'edge';

// 环境变量需在.env文件中配置:NEXT_PUBLIC_BASE_URL=http://localhost:3000(开发)或你的生产域名
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;

export default async function og({ params }) {
  const {id} = params;
  
  // 拼接绝对URL访问public资源
  const imageRes = await fetch(`${BASE_URL}/assets/images/players/png/${id}.png`);
  if (!imageRes.ok) {
    // 处理图片不存在的情况,比如返回默认图
    throw new Error(`Player image ${id} not found`);
  }
  const image = await imageRes.arrayBuffer();

  const interBoldRes = await fetch(`${BASE_URL}/assets/fonts/Inter-Bold.ttf`);
  const interBold = await interBoldRes.arrayBuffer();
  
  return new ImageResponse(
    <div
      style={{
        height: '100%',
        width: '100%',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: '#fff',
        fontSize: 52,
        fontWeight: 600,
        padding: '15px 20px',
        color: '#1e2124',
        margin: '0 auto',
      }}
    >
      {/* @ts-ignore */}
      <img src={image} width="425" height="430" alt={id} style={{ margin: '0 0 15px 0' }}/>
    </div>
  , {
    ...size,
    fonts: [
      {
        name: 'Inter',
        data: interBold,
        style: 'normal',
        weight: 600,
      },
    ]
  }
  );
}

方法2:预生成静态资源路径(适用固定玩家列表)

如果玩家列表是固定的,可在构建时通过静态导入的方式预加载所有玩家图片,运行时直接匹配:

// 预导入所有玩家图片(需确保路径正确)
const playerImages = {
  player1: await import('../../../../public/assets/images/players/png/player1.png'),
  player2: await import('../../../../public/assets/images/players/png/player2.png'),
  // 其他玩家...
};

// 运行时根据id获取对应图片
const imageUrl = playerImages[id].default;
const imageRes = await fetch(imageUrl);
const image = await imageRes.arrayBuffer();

这种方法适合玩家数量固定且较少的场景,能避免动态路径的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:23:23