如何通过 NestJS 13 路由处理程序传输存储在 GCP Cloud Storage 中的文件

问题描述 投票:0回答:1

我有一个非常标准的问题 - 我希望我的 NextJS 应用程序能够提供存储在 GCP Cloud Storage 中的图像,但我想保护这些图像,并且仅对我的 Web 应用程序的授权用户可用,因此存储这些图像的存储桶是私人的。

我知道有两种方法可以解决这个问题:

1.使用签名网址。

我可以使用 GCP 的 Javascript SDK 轻松为我想要提供的每个图像创建签名 URL。一旦你获得了 GCP 凭证,这非常容易,但签名本身实际上在计算上相当昂贵。我在 Cloud Run 中的 1vCPU 实例上运行此应用程序,仅签名就花费了 100 毫秒到 200 毫秒。

最重要的是,签名的 URL 确实会影响您的缓存选项。不理想。

2.创建您自己的 API 端点,用于从 GCP 传输图像

使用此选项,我们可以简单地将图像从 GCP 方向传递到 HTTP 请求者。只要 Cloud Run 和 Cloud Storage 之间的延迟较低,假设流处理不会对 CPU 造成太大负担,这就会恢复我所有的缓存选项,并且是我的首选。

NextJS 13 的文档总体来说相当不错,但我发现 streaming 部分 有点不足以满足我的需求。我正在寻找有关如何在我的应用程序中执行此操作的具体示例。

google-cloud-platform google-cloud-storage next.js13
1个回答
0
投票

经过一番挖掘和实验,我想出了这个,并且很好地完成了工作。

// File location: app/api/image/[id]/route.ts

import { Storage } from '@google-cloud/storage';
import { NextResponse } from 'next/server';

export const dynamic = 'force-dynamic';

const storage = new Storage({
  projectId: '$PROJECT_ID',
});

interface GCPFileStream {
  size: number;
  name: string;
  contentType: string;
  stream: ReadableStream;
}

interface RequestParams {
  params: {
    id: string;
  };
}

// Our HTTP handler
export async function GET(request: Request, { params }: RequestParams): Promise<Response> {
  // NOTE: Don't forget to check auth, either in the handler or some middleware

  const fileStream = await readFile(Number(params.id));

  return new NextResponse(fileStream.stream, {
    status: 200,
    headers: new Headers({
      'Cache-Control': 'max-age=2592000, stale-while-revalidate=86400, private',
      'Content-Type': fileStream.contentType,
      'Content-Length': fileStream.size.toString(),
    }),
  });
}

// The helper function to read the file from GCP
async function readFile(imageId: number): Promise<GCPFileStream> {
  const bucketName = '$BUCKET_NAME';
  const fileName = `${imageId}.jpg`;

  // fetch file size, content type, etc
  const fileMetadata = await storage.bucket(bucketName).file(fileName).getMetadata();
  const stats = fileMetadata[0];

  const readStream = storage.bucket(bucketName).file(fileName).createReadStream();

  return {
    size: Number(stats.size) || 0,
    name: stats.name || '',
    contentType: stats.contentType || '',
    stream: readStream as unknown as ReadableStream,
  };
}

欢迎提出改进建议!

© www.soinside.com 2019 - 2024. All rights reserved.