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

NextJS:如何将ReadableStream<Uint8Array>导入NextApiResponse并代理谷歌图片

解决Next.js API路由转发Google Place图片的问题

核心问题分析

你之前的尝试失败主要有两个原因:

  • Google Place Photo API必须指定maxwidth或maxheight参数,你的请求URL缺少这个必填项,导致API返回错误
  • 对Next.js API路由的响应流处理方式错误,不同Runtime(Node.js/Edge)的流处理逻辑不同

解决方案

1. Pages Router(Node.js Runtime)

创建pages/api/photos/[photoRef].ts文件,代码如下:

import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  // 从URL参数获取图片引用ID
  const photoRef = req.query.photoRef as string;
  if (!photoRef) {
    return res.status(400).send('缺少图片引用ID');
  }

  const apiKey = process.env.GOOGLE_PLACE_API_KEY;
  if (!apiKey) {
    return res.status(500).send('未配置Google API密钥');
  }

  // 构建合规的Google图片请求URL
  const googleUrl = new URL('https://maps.googleapis.com/maps/api/place/photo');
  googleUrl.searchParams.set('photo_reference', photoRef);
  googleUrl.searchParams.set('maxwidth', '800'); // 根据需求调整尺寸
  googleUrl.searchParams.set('key', apiKey);

  try {
    const googleRes = await fetch(googleUrl.toString());

    if (!googleRes.ok) {
      console.error('Google API请求失败:', googleRes.status, googleRes.statusText);
      return res.status(googleRes.status).send('获取图片失败');
    }

    // 复制Google响应的关键头信息
    const contentType = googleRes.headers.get('Content-Type');
    const contentLength = googleRes.headers.get('Content-Length');
    if (contentType) res.setHeader('Content-Type', contentType);
    if (contentLength) res.setHeader('Content-Length', contentLength);

    // Node.js环境下直接将响应流管道到前端
    (googleRes.body as any).pipe(res);
  } catch (err) {
    console.error('转发图片出错:', err);
    res.status(500).send('服务器内部错误');
  }
}

2. App Router(Edge Runtime)

创建app/api/photos/[photoRef]/route.ts文件,代码如下:

import type { NextRequest } from 'next/server';

export const runtime = 'edge'; // 指定使用Edge Runtime

export async function GET(request: NextRequest) {
  // 从URL路径提取图片引用ID
  const photoRef = request.nextUrl.pathname.split('/').pop();
  if (!photoRef) {
    return new Response('缺少图片引用ID', { status: 400 });
  }

  const apiKey = process.env.GOOGLE_PLACE_API_KEY;
  if (!apiKey) {
    return new Response('未配置Google API密钥', { status: 500 });
  }

  // 构建合规的Google图片请求URL
  const googleUrl = new URL('https://maps.googleapis.com/maps/api/place/photo');
  googleUrl.searchParams.set('photo_reference', photoRef);
  googleUrl.searchParams.set('maxwidth', '800');
  googleUrl.searchParams.set('key', apiKey);

  const googleRes = await fetch(googleUrl.toString());

  if (!googleRes.ok) {
    return new Response('获取图片失败', { status: googleRes.status });
  }

  // Edge Runtime直接转发Google的响应流
  return new Response(googleRes.body, {
    headers: {
      'Content-Type': googleRes.headers.get('Content-Type') || 'image/jpeg',
      'Content-Length': googleRes.headers.get('Content-Length') || '',
    },
  });
}

前端使用方式

直接按你的需求调用API即可:

<img src={`/api/photos/${place?.photos[0].photo_reference}`} alt="地点图片" />

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 05:55:26