【问题标题】:Is there a way to pipe ReadableStream<Uint8Array> into NextApiResponse?有没有办法将 ReadableStream<Uint8Array> 导入 NextApiResponse?
【发布时间】:2022-10-18 18:39:44
【问题描述】:

我想在我的 NextJS 应用程序中显示 Google Place Photos。要从它们指定的 URL 获取这些图像,需要一个 API 密钥,但同时,我不想将此 API 密钥公开给公众。

我的目标是实现一个 NextJS API 路由,该路由从 Google Places Photos 中获取并返回指定的图像,同时也能够直接从图像标签中访问,如下所示:

<img src={`/api/photos/${place?.photos[0].photo_reference}`} alt='' />

我在网上找到了一些不同的来源,建议我将 google 请求中的响应流直接传输到传出响应的响应流,如下所示:

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const response = await fetch(
    `https://maps.googleapis.com/maps/api/place/photo
  &photo_reference=${id}
  &key=${process.env.GOOGLE_PLACE_API_KEY}`,
  );

  if (!response.ok) {
    console.log(response);
    res.status(500).end();
    return;
  }

  response.body.pipe(res);
}

但是由于 response.body 是 ReadableStream,它没有 .pipe() 函数。相反,它有 .pipeTo() 和 .pipeThrough()。

然后我尝试了

response.body.pipeTo(res);

但是,这也不起作用,因为 res 是 NextApiResponse 而不是 WritableStream。虽然我在网上搜索过,但我还没有找到类似于 WritableStreams 的写入 NextApiResponse 的方法。

最后,我尝试手动将响应转换为缓冲区并将其写入 NextApiResponse,如下所示:

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const response = await fetch(
    `https://maps.googleapis.com/maps/api/place/photo
  &photo_reference=${id}
  &key=${process.env.GOOGLE_PLACE_API_KEY}`,
  );

  if (!response.ok) {
    console.log(response);
    res.status(500).end();
    return;
  }

  const resBlob = await response.blob();
  const resBufferArray = await resBlob.arrayBuffer();
  const resBuffer = Buffer.from(resBufferArray);

  const fileType = await fileTypeFromBuffer(resBuffer);
  res.setHeader('Content-Type', fileType?.mime ?? 'application/octet-stream');
  res.setHeader('Content-Length', resBuffer.length);
  res.write(resBuffer, 'binary');
  res.end();
}

虽然这完成了响应,但没有显示任何图像。

如何将检索到的谷歌地点图像从服务器直接传递到前端,以便标签可以使用它?

【问题讨论】:

    标签: javascript node.js google-maps-api-3 next.js node-fetch


    【解决方案1】:
    1. 如果您使用 Vercel,则可以直接从 Edge 进行流式传输。您可以将 API 路由转换为使用 experimental-edgeruntume
      // pages/api/png.ts 
      
      import { NextResponse, type NextRequest } from 'next/server'
      
      export const config = {
        runtime: 'experimental-edge',
      }
      
      // Streamable content
      const RESOURCE_URL =
        'https://mdn.github.io/dom-examples/streams/png-transform-stream/png-logo.png'
      
      export default async function handler(request: NextRequest) {
        const r = await fetch(RESOURCE_URL)
      
        const reader = r.body.getReader()
      
        const response = new ReadableStream({
          async start(controller) {
            while (true) {
              const { done, value } = await reader.read()
      
              // When no more data needs to be consumed, break the reading
              if (done) {
                break
              }
      
              // Enqueue the next data chunk into our target stream
              controller.enqueue(value)
            }
      
            // Close the stream
            controller.close()
            reader.releaseLock()
          },
        })
      
        return new Response(response)
      }
      
      1. 在配置中使用next.js rewrites(通过路径或正则表达式仅传递该ID)
      2. 使用middleware匹配那些url来处理重写,无需传输和流

    【讨论】:

      【解决方案2】:

      对我有用的是简单地将ReadableStream&lt;Uint8Array&gt; 类型的response.body 转换为NodeJS.ReadableStream,然后使用pipe 函数。

      const readableStream = response.body as unknown as NodeJS.ReadableStream;
      readableStream.pipe(res);
      

      【讨论】:

        猜你喜欢
        • 2015-08-24
        • 2017-08-01
        • 2020-09-23
        • 2010-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-04
        相关资源
        最近更新 更多