用node.js上有效地在Amazon S3上汇总文件
#aws #javascript #s3 #zip

简介:
在为用户提供单个软件包中从Amazon S3下载多个文件的能力时,将这些文件进行z缩是一个普遍的要求。但是,在无服务器环境(例如AWS Lambda)中,需要考虑一些存储和内存约束。在本文中,我们将在克服这些限制的同时,使用node.js探索如何有效地将存储在Amazon S3上的zip文件。我们将利用可读和可写的流以及Archiver库,以优化内存使用和存储空间。

先决条件:
在我们深入实施之前,请确保您有以下先决条件:

  • 一个具有访问S3服务的AWS帐户
  • Node.js和NPM(Node Package Manager)安装在您的计算机上
  • JavaScript和AWS概念的基本知识

步骤1:配置AWS和依赖项:
首先,安装必要的依赖项。创建一个新的node.js项目,然后使用NPM安装以下软件包:

  • @aws-sdk/client-s3
  • @aws-sdk/lib-storage
  • @aws-sdk/s3-request-presigner
  • archiver

配置我们的AWS凭据在文件本身或单独的config.js文件中提供以下配置。

const AWS_S3_BUCKET = "<aws_s3_bucket>"

const AWS_CONFIG = {
  credentials: {
    accessKeyId: "<aws_access_key_id>",
    secretAccessKey: "<aws_secret_access_key>",
  },
  region: "<aws_s3_region>",
}

步骤2:来自S3的流数据:
我们首先创建一个称为getReadableStreamFromS3的函数,该函数将S3密钥作为输入。此功能使用GetObjectCommand实用程序从@aws-sdk/client-s3库中获取文件,并将文件作为可读流返回。通过利用流,我们避免将整个文件存储在内存中。

async function getReadableStreamFromS3(s3Key: string) {
  const client = new S3Client(AWS_CONFIG);
  const command = new GetObjectCommand({
    Bucket: AWS_S3_BUCKET,
    Key: s3Key,
  });
  const response = await client.send(command);
  return response.Body;
}

步骤3:将拉链数据上传到S3:
接下来,我们创建一个称为getWritableStreamFromS3的函数,该函数将zipped文件作为输入的目标S3键。此功能利用@aws-sdk/lib-storage库中的Upload实用程序。由于Upload函数不会直接曝光可写的流,因此我们使用Node.js流API的PassThrough对象采用了“传递流”。该对象充当了可写流的代理,并允许我们有效地将zipped数据上传到S3。

function getWritableStreamFromS3(zipFileS3Key: string) {
  let _passthrough = new PassThrough();
  const s3 = new S3(AWS_CONFIG);

  new Upload({
    client: s3,
    params: {
      Bucket: AWS_S3_BUCKET,
      Key: zipFileS3Key,
      Body: _passthrough,
    },
  }).done();

  return _passthrough;
}

步骤4:生成和流zip文件到S3:
在此步骤中,我们创建了一个称为generateAndStreamZipfileToS3的函数,该函数列出了S3键(s3KeyList)和上传zip文件的目标键(zipFileS3Key)。在此功能中,我们使用archiver库来创建Zip Archive。我们通过s3KeyList迭代,使用getReadableStreamFromS3将每个文件作为可读的流获取,然后将其附加到zip档案中。然后,我们使用getWritableStreamFromS3获得了可写的流,然后将拉链档案置于它。最后,我们致电zip.finalize()启动拉链过程。

async function generateAndStreamZipfileToS3(
  s3KeyList: [string],
  zipFileS3Key: string
) {
  try {
    let zip = archiver("zip");

    for (const s3Key of s3KeyList) {
      const s3ReadableStream = await getReadableStreamFromS3(s3Key);
      zip.append(<Readable>s3ReadableStream, { name: s3Key.split("/").pop()! });
    }

    const s3WritableStream = getWritableStreamFromS3(zipFileS3Key);
    zip.pipe(s3WritableStream);
    zip.finalize();

  } catch (error: any) {
    logger.error(`Error in generateAndStreamZipfileToS3 ::: ${error.message}`);
  }
}

步骤5:使用预先签名的URL提供Zipped S3文件:
为了提供对Zipped文件的安全访问,我们可以生成有效性有限的预先签名URL。在此可选步骤中,我们创建了一个称为generatePresignedURLforZip的函数,该函数将zipFileS3Key作为输入。使用@aws-sdk/s3-request-presigner库中的GetObjectCommand实用程序,我们生成了一个预先的URL,该URL在24小时后到期。该URL可以与用户共享,从而使他们可以在指定的时间范围内下载zipped文件。

export async function generatePresignedURLforZip(zipFileS3Key: string) {
  logger.info("Generating Presigned URL for the zip file.");
  const client = new S3Client(AWS_CONFIG);
  const command = new GetObjectCommand({
    Bucket: AWS_S3_BUCKET,
    Key: zipFileS3Key,
  });
  const signedUrl = await getSignedUrl(client, command, {
    expiresIn: 24 * 3600,
  });
  return signedUrl;
}

结论:
通过利用可读和可写的流的功能与Archiver库结合使用,我们可以在无服务器环境中有效地将存储在Amazon S3上的ZIP文件。这种方法最大程度地减少了内存使用和存储约束,从而使我们能够处理大型文件,而不会压倒我们的资源。此外,通过使用预先签名的URL,我们可以在有限的持续时间内安全地与用户共享Zipped文件。下次您需要为用户提供一种从S3下载多个文件的方便方法时,请考虑使用node.js。

实现此解决方案。

记住要处理错误,并将适当的错误处理在您的实际实现中。愉快的编码!

参考: